Merge pull request #114 from emilb/master

Added Maven completion file
diff --git a/.gitignore b/.gitignore
index 32e62de..b27cddd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,9 +1,10 @@
 */enabled/*
 .DS_Store
-custom/*.bash
+custom/*
 !custom/example.bash
 .rvmrc
 aliases/custom.aliases.bash
 lib/custom.bash
 plugins/custom.plugins.bash
 *.swp
+.*.un~
diff --git a/README.md b/README.md
index 0c9c303..105d5be 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 # Bash it
 
-**Bash it** is a mash up of my own bash commands and scripts, other bash stuff I have found. 
+**Bash it** is a mash up of my own bash commands and scripts, other bash stuff I have found.
 
 (And a shameless ripoff of [oh-my-zsh](https://github.com/robbyrussell/oh-my-zsh). :)
 
@@ -13,18 +13,19 @@
 3. Edit your `~/.bash_profile` file in order to customize bash-it.
 
 **NOTE:**
-The install script will also prompt you asking if you use [Jekyll](https://github.com/mojombo/jekyll). 
+The install script will also prompt you asking if you use [Jekyll](https://github.com/mojombo/jekyll).
 This is to set up the `.jekyllconfig` file, which stores info necessary to use the Jekyll plugin.
 
 
 ## Help Screens
 
 ```
-bash-it (will show all the help commands)
-aliases-help
-rails-help
-git-help
-plugins-help
+bash-it show aliases        # shows installed and available aliases
+bash-it show completions    # shows installed and available completions
+bash-it show plugins        # shows installed and available plugins
+bash-it help aliases        # shows help for installed aliases
+bash-it help completions    # shows help for installed completions
+bash-it help plugins        # shows help for installed plugins
 ```
 
 ## Your Custom scripts, aliases, and functions
@@ -45,7 +46,7 @@
 
 I think everyone has their own custom scripts accumulated over time.  And so, following in the footsteps of oh-my-zsh, bash it is a framework for easily customizing your bash shell. Everyone's got a custom toolbox, so let's start making them even better, **as a community!**
 
-Send me a pull request and I'll merge it as long as it looks good. If you change an existing command, please give an explanation why. That will help a lot when I merge your changes in. 
+Send me a pull request and I'll merge it as long as it looks good. If you change an existing command, please give an explanation why. That will help a lot when I merge your changes in.
 
 Thanks, and happing bashing!
 
diff --git a/aliases/available/bundler.aliases.bash b/aliases/available/bundler.aliases.bash
index a7756be..fc20f4f 100644
--- a/aliases/available/bundler.aliases.bash
+++ b/aliases/available/bundler.aliases.bash
@@ -1,21 +1,9 @@
-#!/usr/bin/env bash
+cite 'about-alias'
+about-alias 'ruby bundler'
 
 # Bundler Commands
-alias be="bundle exec"
-alias bi="bundle install"
-alias bl="bundle list"
-alias bu="bundle update"
-alias bp="bundle package"
-
-
-function bundler-help() {
-  echo "Bundler Aliases Usage"
-  echo
-  echo "  be          = bundle exec"
-  echo "  bi          = bundle install"
-  echo "  bl          = bundle list"
-  echo "  bu          = bundle update"
-  echo "  bp          = bundle package"
-  echo
-}
-
+alias be='bundle exec'
+alias bi='bundle install'
+alias bl='bundle list'
+alias bu='bundle update'
+alias bp='bundle package'
diff --git a/aliases/available/emacs.aliases.bash b/aliases/available/emacs.aliases.bash
index a133984..ffdb9f3 100644
--- a/aliases/available/emacs.aliases.bash
+++ b/aliases/available/emacs.aliases.bash
@@ -1,4 +1,5 @@
-#!/usr/bin/env bash
+cite 'about-alias'
+about-alias 'emacs editor'
 
 case $OSTYPE in
   linux*)
@@ -6,6 +7,6 @@
     alias e='emacsclient -n'
     ;;
   darwin*)
-    alias em="open -a emacs"
+    alias em='open -a emacs'
     ;;
 esac
diff --git a/aliases/available/general.aliases.bash b/aliases/available/general.aliases.bash
index 660a8ca..3e87e2d 100644
--- a/aliases/available/general.aliases.bash
+++ b/aliases/available/general.aliases.bash
@@ -1,4 +1,5 @@
-#!/usr/bin/env bash
+cite about-alias
+about-alias 'general aliases'
 
 # List directory contents
 alias sl=ls
@@ -27,20 +28,20 @@
 alias edit="$EDITOR"
 alias pager="$PAGER"
 
-alias q="exit"
+alias q='exit'
 
 alias irc="$IRC_CLIENT"
 
-alias rb="ruby"
+alias rb='ruby'
 
 # Pianobar can be found here: http://github.com/PromyLOPh/pianobar/
 
-alias piano="pianobar"
+alias piano='pianobar'
 
 alias ..='cd ..'         # Go up one directory
 alias ...='cd ../..'     # Go up two directories
 alias ....='cd ../../..' # Go up two directories
-alias -- -="cd -"        # Go back
+alias -- -='cd -'        # Go back
 
 # Shell History
 alias h='history'
@@ -53,30 +54,4 @@
 
 # Directory
 alias	md='mkdir -p'
-alias	rd=rmdir
-
-function aliases-help() {
-echo "Generic Alias Usage"
-echo
-echo "  sl      = ls"
-echo "  ls      = ls -G"
-echo "  la      = ls -AF"
-echo "  ll      = ls -al"
-echo "  l       = ls -a"
-echo "  c/k/cls = clear"
-echo "  ..      = cd .."
-echo "  ...     = cd ../.."
-echo "  -       = cd -"
-echo "  h       = history"
-echo "  md      = mkdir -p"
-echo "  rd      = rmdir"
-echo "  editor  = $EDITOR"
-echo "  pager   = $PAGER"
-echo "  piano   = pianobar"
-echo "  q       = exit"
-echo "  irc     = $IRC_CLIENT"
-echo "  md      = mkdir -p"
-echo "  rd      = rmdir"
-echo "  rb      = ruby"
-echo
-}
+alias	rd='rmdir'
diff --git a/aliases/available/git.aliases.bash b/aliases/available/git.aliases.bash
index abfa996..bbac313 100644
--- a/aliases/available/git.aliases.bash
+++ b/aliases/available/git.aliases.bash
@@ -1,4 +1,5 @@
-#!/usr/bin/env bash
+cite 'about-alias'
+about-alias 'common git abbreviations'
 
 # Aliases
 alias gcl='git clone'
@@ -38,36 +39,3 @@
     alias gd='git diff'
     ;;
 esac
-
-
-
-function git-help() {
-  echo "Git Custom Aliases Usage"
-  echo
-  echo "  gcl	  = git clone"
-  echo "  g       = git"
-  echo "  get 	  = git"
-  echo "  ga      = git add"
-  echo "  gall	  = git add ."
-  echo "  gst/gs  = git status"
-  echo "  gss	  = git status -s"
-  echo "  gl      = git pull"
-  echo "  gup     = git fetch && git rebase"
-  echo "  gp      = git push"
-  echo "  gd      = git diff | mate"
-  echo "  gdv     = git diff -w \"$@\" | vim -R -"
-  echo "  gc      = git commit -v"
-  echo "  gca     = git commit -v -a"
-  echo "  gci 	  = git commit --interactive"
-  echo "  gb      = git branch"
-  echo "  gba     = git branch -a"
-  echo "  gcount  = git shortlog -sn"
-  echo "  gcp     = git cherry-pick"
-  echo "  gco     = git checkout"
-  echo "  gexport = git git archive --format zip --output"
-  echo "  gdel    = git branch -D"
-  echo "  gpo     = git push origin"
-  echo "  gmu     = git fetch origin -v; git fetch upstream -v; git merge upstream/master"
-  echo "  gll     = git log --graph --pretty=oneline --abbrev-commit"
-  echo
-}
diff --git a/aliases/available/heroku.aliases.bash b/aliases/available/heroku.aliases.bash
index 7bc1dbe..a749d42 100644
--- a/aliases/available/heroku.aliases.bash
+++ b/aliases/available/heroku.aliases.bash
@@ -1,4 +1,5 @@
-#!/usr/bin/env bash
+cite 'about-alias'
+about-alias 'heroku task abbreviations'
 
 # heroku
 alias h='heroku'
@@ -31,26 +32,3 @@
 alias hca='heroku config:add'
 alias hcr='heroku config:remove'
 alias hcc='heroku config:clear'
-
-function heroku-help() {
-  echo "Heroku Aliases Usage"
-  echo
-  echo "  h           = heroku"
-  echo "  hl          = heroku list"
-  echo "  hi          = heroku info"
-  echo "  ho          = heroku open"
-  echo "  hd          = heroku dynos"
-  echo "  hw          = heroku workers"
-  echo "  hr          = heroku rake"
-  echo "  hcon        = heroku console"
-  echo "  hnew        = heroku create"
-  echo "  hrestart    = heroku restart"
-  echo "  hlog        = heroku logs"
-  echo "  hon         = heroku maintenance:on"
-  echo "  hoff        = heroku maintenance:off"
-  echo "  hc          = heroku config"
-  echo "  hca         = heroku config:add"
-  echo "  hcr         = heroku config:remove"
-  echo "  hcc         = heroku config:clear"
-  echo
-}
diff --git a/aliases/available/hg.aliases.bash b/aliases/available/hg.aliases.bash
index 245c529..eea819f 100644
--- a/aliases/available/hg.aliases.bash
+++ b/aliases/available/hg.aliases.bash
@@ -1,14 +1,6 @@
-#!/usr/bin/env bash
+cite 'about-alias'
+about-alias 'mercurial abbreviations'
 
 alias hs='hg status'
 alias hsum='hg summary'
 alias hcm='hg commit -m'
-
-function hg-help() {
-  echo "Mercurial Alias Help"
-  echo
-  echo "  hs    = hg status"
-  echo "  hsum  = hg summary"
-  echo "  hcm   = hg commit -m"
-  echo
-}
diff --git a/aliases/available/homebrew.aliases.bash b/aliases/available/homebrew.aliases.bash
index 051081d..b8f1481 100644
--- a/aliases/available/homebrew.aliases.bash
+++ b/aliases/available/homebrew.aliases.bash
@@ -1,24 +1,13 @@
 # Some aliases for Homebrew
 
-alias bup="brew update && brew upgrade"
-alias bout="brew outdated"
-alias bin="brew install"
-alias brm="brew uninstall"
-alias bls="brew list"
-alias bsr="brew search"
-alias binf="brew info"
-alias bdr="brew doctor"
+cite 'about-alias'
+about-alias 'homebrew abbreviations'
 
-function brew-help() {
-  echo "Homebrew Alias Usage"
-  echo
-  echo "bup  = brew update && brew upgrade"
-  echo "bout = brew outdated"
-  echo "bin  = brew install"
-  echo "brm  = brew uninstall"
-  echo "bls  = brew list"
-  echo "bsr  = brew search"
-  echo "binf = brew info"
-  echo "bdr  = brew doctor"
-  echo
-}
+alias bup='brew update && brew upgrade'
+alias bout='brew outdated'
+alias bin='brew install'
+alias brm='brew uninstall'
+alias bls='brew list'
+alias bsr='brew search'
+alias binf='brew info'
+alias bdr='brew doctor'
diff --git a/aliases/available/maven.aliases.bash b/aliases/available/maven.aliases.bash
index 349f9d8..4cd89d2 100644
--- a/aliases/available/maven.aliases.bash
+++ b/aliases/available/maven.aliases.bash
@@ -1,22 +1,11 @@
-alias mci="mvn clean install"
-alias mi="mvn install"
-alias mrprep="mvn release:prepare"
-alias mrperf="mvn release:perform"
-alias mrrb="mvn release:rollback"
-alias mdep="mvn dependency:tree"
-alias mpom="mvn help:effective-pom"
-alias mcisk="mci -Dmaven.test.skip=true"
+cite 'about-alias'
+about-alias 'maven abbreviations'
 
-function maven-help() {
-  echo "Maven Custom Aliases Usage"
-  echo
-  echo "  mci    = mvn clean install"
-  echo "  mi     = mvn install"
-  echo "  mrprep = mvn release:prepare"
-  echo "  mrperf = mvn release:perform"
-  echo "  mrrb   = mvn release:rollback"
-  echo "  mdep   = mvn dependency:tree"
-  echo "  mpom   = mvn help:effective-pom"
-  echo "  mcisk  = mvn clean install -Dmaven.test.skip=true"  
-  echo
-}
+alias mci='mvn clean install'
+alias mi='mvn install'
+alias mrprep='mvn release:prepare'
+alias mrperf='mvn release:perform'
+alias mrrb='mvn release:rollback'
+alias mdep='mvn dependency:tree'
+alias mpom='mvn help:effective-pom'
+alias mcisk='mci -Dmaven.test.skip=true'
diff --git a/aliases/available/osx.aliases.bash b/aliases/available/osx.aliases.bash
index 008b67c..ffe3ca0 100644
--- a/aliases/available/osx.aliases.bash
+++ b/aliases/available/osx.aliases.bash
@@ -1,4 +1,5 @@
-#!/usr/bin/env bash
+cite 'about-alias'
+about-alias 'osx-specific aliases'
 
 # Desktop Programs
 alias fireworks="open -a '/Applications/Adobe Fireworks CS3/Adobe Fireworks CS3.app'"
@@ -18,3 +19,6 @@
 if [ -s /usr/bin/firefox ] ; then
   unalias firefox
 fi
+
+# Requires growlnotify, which can be found in the Growl DMG under "Extras"
+alias grnot='growlnotify -s -t Terminal -m "Done"'
diff --git a/aliases/available/rails.aliases.bash b/aliases/available/rails.aliases.bash
index 1d7b310..ae664bf 100644
--- a/aliases/available/rails.aliases.bash
+++ b/aliases/available/rails.aliases.bash
@@ -1,4 +1,5 @@
-#!/usr/bin/env bash
+cite 'about-alias'
+about-alias 'rails abbreviations'
 
 # Rails Commands
 alias r='rails'
@@ -19,24 +20,3 @@
 alias restart='touch tmp/restart.txt'  # restart passenger
 alias devlog='tail -f log/development.log'
 alias taild='tail -f log/development.log' # tail dev log
-
-function rails-help() {
-  echo "Rails Aliases Usage"
-  echo
-  echo "  r           = rails"
-  echo "  rg          = rails generate"
-  echo "  rs/ss       = rails server"
-  echo "  ts          = thin server"
-  echo "  rc/sc       = rails console"
-  echo "  rn          = rails new"
-  echo "  rd          = rails dbconsole"
-  echo "  rp          = rails plugin"
-  echo "  ra          = rails application"
-  echo "  rd          = rails destroy"
-  echo "  restartapp  = touch tmp/restart.txt"
-  echo "  restart     = touch tmp/restart.txt"
-  echo "  devlog      = tail -f log/development.log"
-  echo "  taild       = tail -f log/development.log"
-  echo
-}
-
diff --git a/aliases/available/textmate.aliases.bash b/aliases/available/textmate.aliases.bash
index 897c7d3..f0f69e4 100644
--- a/aliases/available/textmate.aliases.bash
+++ b/aliases/available/textmate.aliases.bash
@@ -1,4 +1,5 @@
-#!/usr/bin/env bash
+cite 'about-alias'
+about-alias 'textmate abbreviations'
 
 case $OSTYPE in
   darwin*)
diff --git a/aliases/available/todo.txt-cli.aliases.bash b/aliases/available/todo.txt-cli.aliases.bash
index ce27716..5bf35d0 100644
--- a/aliases/available/todo.txt-cli.aliases.bash
+++ b/aliases/available/todo.txt-cli.aliases.bash
@@ -1,19 +1,8 @@
-#!/usr/bin/env bash
+cite 'about-alias'
+about-alias 'todo.txt-cli abbreviations'
 
 alias tls="$TODO ls"
 alias ta="$TODO a"
 alias trm="$TODO rm"
 alias tdo="$TODO do"
 alias tpri="$TODO pri"
-
-todo-help() {
-	echo
-	echo "todo.txt-cli Custom Aliases Usage"
-	echo
-	echo " tls  = $TODO ls"
-	echo " ta   = $TODO add"
-	echo " trm  = $TODO rm"
-	echo " tdo  = $TODO do"
-	echo " tpri = $TODO pri"
-	echo
-}
diff --git a/aliases/available/vim.aliases.bash b/aliases/available/vim.aliases.bash
index c156ffd..c14f32b 100644
--- a/aliases/available/vim.aliases.bash
+++ b/aliases/available/vim.aliases.bash
@@ -1,3 +1,4 @@
-#!/usr/bin/env bash
+cite 'about-alias'
+about-alias 'vim abbreviations'
 
 alias v='mvim --remote-tab'
diff --git a/bash_it.sh b/bash_it.sh
old mode 100644
new mode 100755
index 8564c88..226213f
--- a/bash_it.sh
+++ b/bash_it.sh
@@ -21,6 +21,12 @@
     unset $BASH_THEME;
 fi
 
+# Load composure first, so we support function metadata
+source "${BASH_IT}/lib/composure.sh"
+
+# support 'plumbing' metadata
+cite _about _param _example _group _author _version
+
 # Load colors first so they can be use in base theme
 source "${BASH_IT}/themes/colors.theme.bash"
 source "${BASH_IT}/themes/base.theme.bash"
@@ -69,21 +75,3 @@
 then
   . $HOME/.jekyllconfig
 fi
-
-
-#
-# Custom Help
-
-function bash-it() {
-  echo "Welcome to Bash It!"
-  echo
-  echo "Here is a list of commands you can use to get help screens for specific pieces of Bash it:"
-  echo
-  echo "  rails-help                  This will list out all the aliases you can use with rails."
-  echo "  git-help                    This will list out all the aliases you can use with git."
-  echo "  todo-help                   This will list out all the aliases you can use with todo.txt-cli"
-  echo "  brew-help                   This will list out all the aliases you can use with Homebrew"
-  echo "  aliases-help                Generic list of aliases."
-  echo "  plugins-help                This will list out all the plugins and functions you can use with bash-it"
-  echo
-}
diff --git a/completion/available/git.completion.bash b/completion/available/git.completion.bash
index 35dbf04..1496c6d 100644
--- a/completion/available/git.completion.bash
+++ b/completion/available/git.completion.bash
@@ -1,6 +1,6 @@
 #!bash
 #
-# bash completion support for core Git.
+# bash/zsh completion support for core Git.
 #
 # Copyright (C) 2006,2007 Shawn O. Pearce <spearce@spearce.org>
 # Conceptually based on gitcompletion (http://gitweb.hawaga.org.uk/).
@@ -18,27 +18,48 @@
 # To use these routines:
 #
 #    1) Copy this file to somewhere (e.g. ~/.git-completion.sh).
-#    2) Added the following line to your .bashrc:
+#    2) Add the following line to your .bashrc/.zshrc:
 #        source ~/.git-completion.sh
 #
-#    3) You may want to make sure the git executable is available
-#       in your PATH before this script is sourced, as some caching
-#       is performed while the script loads.  If git isn't found
-#       at source time then all lookups will be done on demand,
-#       which may be slightly slower.
-#
-#    4) Consider changing your PS1 to also show the current branch:
-#        PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '
+#    3) Consider changing your PS1 to also show the current branch:
+#         Bash: PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '
+#         ZSH:  PS1='[%n@%m %c$(__git_ps1 " (%s)")]\$ '
 #
 #       The argument to __git_ps1 will be displayed only if you
 #       are currently in a git repository.  The %s token will be
 #       the name of the current branch.
 #
-#	In addition, if you set GIT_PS1_SHOWDIRTYSTATE to a nonempty
-#	value, unstaged (*) and staged (+) changes will be shown next
-#	to the branch name.  You can configure this per-repository
-#	with the bash.showDirtyState variable, which defaults to true
-#	once GIT_PS1_SHOWDIRTYSTATE is enabled.
+#       In addition, if you set GIT_PS1_SHOWDIRTYSTATE to a nonempty
+#       value, unstaged (*) and staged (+) changes will be shown next
+#       to the branch name.  You can configure this per-repository
+#       with the bash.showDirtyState variable, which defaults to true
+#       once GIT_PS1_SHOWDIRTYSTATE is enabled.
+#
+#       You can also see if currently something is stashed, by setting
+#       GIT_PS1_SHOWSTASHSTATE to a nonempty value. If something is stashed,
+#       then a '$' will be shown next to the branch name.
+#
+#       If you would like to see if there're untracked files, then you can
+#       set GIT_PS1_SHOWUNTRACKEDFILES to a nonempty value. If there're
+#       untracked files, then a '%' will be shown next to the branch name.
+#
+#       If you would like to see the difference between HEAD and its
+#       upstream, set GIT_PS1_SHOWUPSTREAM="auto".  A "<" indicates
+#       you are behind, ">" indicates you are ahead, and "<>"
+#       indicates you have diverged.  You can further control
+#       behaviour by setting GIT_PS1_SHOWUPSTREAM to a space-separated
+#       list of values:
+#           verbose       show number of commits ahead/behind (+/-) upstream
+#           legacy        don't use the '--count' option available in recent
+#                         versions of git-rev-list
+#           git           always compare HEAD to @{upstream}
+#           svn           always compare HEAD to your SVN upstream
+#       By default, __git_ps1 will compare HEAD to your SVN upstream
+#       if it can find one, or @{upstream} otherwise.  Once you have
+#       set GIT_PS1_SHOWUPSTREAM, you can override it on a
+#       per-repository basis by setting the bash.showUpstream config
+#       variable.
+#
 #
 # To submit patches:
 #
@@ -52,6 +73,10 @@
 #       git@vger.kernel.org
 #
 
+if [[ -n ${ZSH_VERSION-} ]]; then
+	autoload -U +X bashcompinit && bashcompinit
+fi
+
 case "$COMP_WORDBREAKS" in
 *:*) : great ;;
 *)   COMP_WORDBREAKS="$COMP_WORDBREAKS:"
@@ -62,7 +87,7 @@
 __gitdir ()
 {
 	if [ -z "${1-}" ]; then
-		if [ -n "$__git_dir" ]; then
+		if [ -n "${__git_dir-}" ]; then
 			echo "$__git_dir"
 		elif [ -d .git ]; then
 			echo .git
@@ -76,73 +101,219 @@
 	fi
 }
 
+# stores the divergence from upstream in $p
+# used by GIT_PS1_SHOWUPSTREAM
+__git_ps1_show_upstream ()
+{
+	local key value
+	local svn_remote=() svn_url_pattern count n
+	local upstream=git legacy="" verbose=""
+
+	# get some config options from git-config
+	local output="$(git config -z --get-regexp '^(svn-remote\..*\.url|bash\.showupstream)$' 2>/dev/null | tr '\0\n' '\n ')"
+	while read -r key value; do
+		case "$key" in
+		bash.showupstream)
+			GIT_PS1_SHOWUPSTREAM="$value"
+			if [[ -z "${GIT_PS1_SHOWUPSTREAM}" ]]; then
+				p=""
+				return
+			fi
+			;;
+		svn-remote.*.url)
+			svn_remote[ $((${#svn_remote[@]} + 1)) ]="$value"
+			svn_url_pattern+="\\|$value"
+			upstream=svn+git # default upstream is SVN if available, else git
+			;;
+		esac
+	done <<< "$output"
+
+	# parse configuration values
+	for option in ${GIT_PS1_SHOWUPSTREAM}; do
+		case "$option" in
+		git|svn) upstream="$option" ;;
+		verbose) verbose=1 ;;
+		legacy)  legacy=1  ;;
+		esac
+	done
+
+	# Find our upstream
+	case "$upstream" in
+	git)    upstream="@{upstream}" ;;
+	svn*)
+		# get the upstream from the "git-svn-id: ..." in a commit message
+		# (git-svn uses essentially the same procedure internally)
+		local svn_upstream=($(git log --first-parent -1 \
+					--grep="^git-svn-id: \(${svn_url_pattern#??}\)" 2>/dev/null))
+		if [[ 0 -ne ${#svn_upstream[@]} ]]; then
+			svn_upstream=${svn_upstream[ ${#svn_upstream[@]} - 2 ]}
+			svn_upstream=${svn_upstream%@*}
+			local n_stop="${#svn_remote[@]}"
+			for ((n=1; n <= n_stop; ++n)); do
+				svn_upstream=${svn_upstream#${svn_remote[$n]}}
+			done
+
+			if [[ -z "$svn_upstream" ]]; then
+				# default branch name for checkouts with no layout:
+				upstream=${GIT_SVN_ID:-git-svn}
+			else
+				upstream=${svn_upstream#/}
+			fi
+		elif [[ "svn+git" = "$upstream" ]]; then
+			upstream="@{upstream}"
+		fi
+		;;
+	esac
+
+	# Find how many commits we are ahead/behind our upstream
+	if [[ -z "$legacy" ]]; then
+		count="$(git rev-list --count --left-right \
+				"$upstream"...HEAD 2>/dev/null)"
+	else
+		# produce equivalent output to --count for older versions of git
+		local commits
+		if commits="$(git rev-list --left-right "$upstream"...HEAD 2>/dev/null)"
+		then
+			local commit behind=0 ahead=0
+			for commit in $commits
+			do
+				case "$commit" in
+				"<"*) let ++behind
+					;;
+				*)    let ++ahead
+					;;
+				esac
+			done
+			count="$behind	$ahead"
+		else
+			count=""
+		fi
+	fi
+
+	# calculate the result
+	if [[ -z "$verbose" ]]; then
+		case "$count" in
+		"") # no upstream
+			p="" ;;
+		"0	0") # equal to upstream
+			p="=" ;;
+		"0	"*) # ahead of upstream
+			p=">" ;;
+		*"	0") # behind upstream
+			p="<" ;;
+		*)	    # diverged from upstream
+			p="<>" ;;
+		esac
+	else
+		case "$count" in
+		"") # no upstream
+			p="" ;;
+		"0	0") # equal to upstream
+			p=" u=" ;;
+		"0	"*) # ahead of upstream
+			p=" u+${count#0	}" ;;
+		*"	0") # behind upstream
+			p=" u-${count%	0}" ;;
+		*)	    # diverged from upstream
+			p=" u+${count#*	}-${count%	*}" ;;
+		esac
+	fi
+
+}
+
+
 # __git_ps1 accepts 0 or 1 arguments (i.e., format string)
 # returns text to add to bash PS1 prompt (includes branch name)
 __git_ps1 ()
 {
-	local g="$(git rev-parse --git-dir 2>/dev/null)"
+	local g="$(__gitdir)"
 	if [ -n "$g" ]; then
-		local r
-		local b
-		if [ -d "$g/rebase-apply" ]
-		then
-			if test -f "$g/rebase-apply/rebasing"
-			then
-				r="|REBASE"
-			elif test -f "$g/rebase-apply/applying"
-			then
-				r="|AM"
-			else
-				r="|AM/REBASE"
-			fi
-			b="$(git symbolic-ref HEAD 2>/dev/null)"
-		elif [ -f "$g/rebase-merge/interactive" ]
-		then
+		local r=""
+		local b=""
+		if [ -f "$g/rebase-merge/interactive" ]; then
 			r="|REBASE-i"
 			b="$(cat "$g/rebase-merge/head-name")"
-		elif [ -d "$g/rebase-merge" ]
-		then
+		elif [ -d "$g/rebase-merge" ]; then
 			r="|REBASE-m"
 			b="$(cat "$g/rebase-merge/head-name")"
-		elif [ -f "$g/MERGE_HEAD" ]
-		then
-			r="|MERGING"
-			b="$(git symbolic-ref HEAD 2>/dev/null)"
 		else
-			if [ -f "$g/BISECT_LOG" ]
-			then
+			if [ -d "$g/rebase-apply" ]; then
+				if [ -f "$g/rebase-apply/rebasing" ]; then
+					r="|REBASE"
+				elif [ -f "$g/rebase-apply/applying" ]; then
+					r="|AM"
+				else
+					r="|AM/REBASE"
+				fi
+			elif [ -f "$g/MERGE_HEAD" ]; then
+				r="|MERGING"
+			elif [ -f "$g/CHERRY_PICK_HEAD" ]; then
+				r="|CHERRY-PICKING"
+			elif [ -f "$g/BISECT_LOG" ]; then
 				r="|BISECTING"
 			fi
-			if ! b="$(git symbolic-ref HEAD 2>/dev/null)"
-			then
-				if ! b="$(git describe --exact-match HEAD 2>/dev/null)"
-				then
-					b="$(cut -c1-7 "$g/HEAD")..."
+
+			b="$(git symbolic-ref HEAD 2>/dev/null)" || {
+
+				b="$(
+				case "${GIT_PS1_DESCRIBE_STYLE-}" in
+				(contains)
+					git describe --contains HEAD ;;
+				(branch)
+					git describe --contains --all HEAD ;;
+				(describe)
+					git describe HEAD ;;
+				(* | default)
+					git describe --tags --exact-match HEAD ;;
+				esac 2>/dev/null)" ||
+
+				b="$(cut -c1-7 "$g/HEAD" 2>/dev/null)..." ||
+				b="unknown"
+				b="($b)"
+			}
+		fi
+
+		local w=""
+		local i=""
+		local s=""
+		local u=""
+		local c=""
+		local p=""
+
+		if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then
+			if [ "true" = "$(git rev-parse --is-bare-repository 2>/dev/null)" ]; then
+				c="BARE:"
+			else
+				b="GIT_DIR!"
+			fi
+		elif [ "true" = "$(git rev-parse --is-inside-work-tree 2>/dev/null)" ]; then
+			if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ]; then
+				if [ "$(git config --bool bash.showDirtyState)" != "false" ]; then
+					git diff --no-ext-diff --quiet --exit-code || w="*"
+					if git rev-parse --quiet --verify HEAD >/dev/null; then
+						git diff-index --cached --quiet HEAD -- || i="+"
+					else
+						i="#"
+					fi
 				fi
 			fi
+			if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ]; then
+			        git rev-parse --verify refs/stash >/dev/null 2>&1 && s="$"
+			fi
+
+			if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ]; then
+			   if [ -n "$(git ls-files --others --exclude-standard)" ]; then
+			      u="%"
+			   fi
+			fi
+
+			if [ -n "${GIT_PS1_SHOWUPSTREAM-}" ]; then
+				__git_ps1_show_upstream
+			fi
 		fi
 
-		local w
-		local i
-
-		if test -n "$GIT_PS1_SHOWDIRTYSTATE"; then
-			if test "$(git config --bool bash.showDirtyState)" != "false"; then
-				git diff --no-ext-diff --ignore-submodules \
-					--quiet --exit-code || w="*"
-				if git rev-parse --quiet --verify HEAD >/dev/null; then
-					git diff-index --cached --quiet \
-						--ignore-submodules HEAD -- || i="+"
-				else
-					i="#"
-				fi
-			fi
-		fi
-
-		if [ -n "${1-}" ]; then
-			printf "$1" "${b##refs/heads/}$w$i$r"
-		else
-			printf " (%s)" "${b##refs/heads/}$w$i$r"
-		fi
+		local f="$w$i$s$u"
+		printf "${1:- (%s)}" "$c${b##refs/heads/}${f:+ $f}$r$p"
 	fi
 }
 
@@ -159,15 +330,177 @@
 	done
 }
 
-# __gitcomp accepts 1, 2, 3, or 4 arguments
-# generates completion reply with compgen
+# The following function is based on code from:
+#
+#   bash_completion - programmable completion functions for bash 3.2+
+#
+#   Copyright © 2006-2008, Ian Macdonald <ian@caliban.org>
+#             © 2009-2010, Bash Completion Maintainers
+#                     <bash-completion-devel@lists.alioth.debian.org>
+#
+#   This program is free software; you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as published by
+#   the Free Software Foundation; either version 2, or (at your option)
+#   any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with this program; if not, write to the Free Software Foundation,
+#   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+#   The latest version of this software can be obtained here:
+#
+#   http://bash-completion.alioth.debian.org/
+#
+#   RELEASE: 2.x
+
+# This function can be used to access a tokenized list of words
+# on the command line:
+#
+#	__git_reassemble_comp_words_by_ref '=:'
+#	if test "${words_[cword_-1]}" = -w
+#	then
+#		...
+#	fi
+#
+# The argument should be a collection of characters from the list of
+# word completion separators (COMP_WORDBREAKS) to treat as ordinary
+# characters.
+#
+# This is roughly equivalent to going back in time and setting
+# COMP_WORDBREAKS to exclude those characters.  The intent is to
+# make option types like --date=<type> and <rev>:<path> easy to
+# recognize by treating each shell word as a single token.
+#
+# It is best not to set COMP_WORDBREAKS directly because the value is
+# shared with other completion scripts.  By the time the completion
+# function gets called, COMP_WORDS has already been populated so local
+# changes to COMP_WORDBREAKS have no effect.
+#
+# Output: words_, cword_, cur_.
+
+__git_reassemble_comp_words_by_ref()
+{
+	local exclude i j first
+	# Which word separators to exclude?
+	exclude="${1//[^$COMP_WORDBREAKS]}"
+	cword_=$COMP_CWORD
+	if [ -z "$exclude" ]; then
+		words_=("${COMP_WORDS[@]}")
+		return
+	fi
+	# List of word completion separators has shrunk;
+	# re-assemble words to complete.
+	for ((i=0, j=0; i < ${#COMP_WORDS[@]}; i++, j++)); do
+		# Append each nonempty word consisting of just
+		# word separator characters to the current word.
+		first=t
+		while
+			[ $i -gt 0 ] &&
+			[ -n "${COMP_WORDS[$i]}" ] &&
+			# word consists of excluded word separators
+			[ "${COMP_WORDS[$i]//[^$exclude]}" = "${COMP_WORDS[$i]}" ]
+		do
+			# Attach to the previous token,
+			# unless the previous token is the command name.
+			if [ $j -ge 2 ] && [ -n "$first" ]; then
+				((j--))
+			fi
+			first=
+			words_[$j]=${words_[j]}${COMP_WORDS[i]}
+			if [ $i = $COMP_CWORD ]; then
+				cword_=$j
+			fi
+			if (($i < ${#COMP_WORDS[@]} - 1)); then
+				((i++))
+			else
+				# Done.
+				return
+			fi
+		done
+		words_[$j]=${words_[j]}${COMP_WORDS[i]}
+		if [ $i = $COMP_CWORD ]; then
+			cword_=$j
+		fi
+	done
+}
+
+if ! type _get_comp_words_by_ref >/dev/null 2>&1; then
+if [[ -z ${ZSH_VERSION:+set} ]]; then
+_get_comp_words_by_ref ()
+{
+	local exclude cur_ words_ cword_
+	if [ "$1" = "-n" ]; then
+		exclude=$2
+		shift 2
+	fi
+	__git_reassemble_comp_words_by_ref "$exclude"
+	cur_=${words_[cword_]}
+	while [ $# -gt 0 ]; do
+		case "$1" in
+		cur)
+			cur=$cur_
+			;;
+		prev)
+			prev=${words_[$cword_-1]}
+			;;
+		words)
+			words=("${words_[@]}")
+			;;
+		cword)
+			cword=$cword_
+			;;
+		esac
+		shift
+	done
+}
+else
+_get_comp_words_by_ref ()
+{
+	while [ $# -gt 0 ]; do
+		case "$1" in
+		cur)
+			cur=${COMP_WORDS[COMP_CWORD]}
+			;;
+		prev)
+			prev=${COMP_WORDS[COMP_CWORD-1]}
+			;;
+		words)
+			words=("${COMP_WORDS[@]}")
+			;;
+		cword)
+			cword=$COMP_CWORD
+			;;
+		-n)
+			# assume COMP_WORDBREAKS is already set sanely
+			shift
+			;;
+		esac
+		shift
+	done
+}
+fi
+fi
+
+# Generates completion reply with compgen, appending a space to possible
+# completion words, if necessary.
+# It accepts 1 to 4 arguments:
+# 1: List of possible completion words.
+# 2: A prefix to be added to each possible completion word (optional).
+# 3: Generate possible completion matches for this word (optional).
+# 4: A suffix to be appended to each possible completion word (optional).
 __gitcomp ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
+	local cur_="$cur"
+
 	if [ $# -gt 2 ]; then
-		cur="$3"
+		cur_="$3"
 	fi
-	case "$cur" in
+	case "$cur_" in
 	--*=)
 		COMPREPLY=()
 		;;
@@ -175,80 +508,118 @@
 		local IFS=$'\n'
 		COMPREPLY=($(compgen -P "${2-}" \
 			-W "$(__gitcomp_1 "${1-}" "${4-}")" \
-			-- "$cur"))
+			-- "$cur_"))
 		;;
 	esac
 }
 
-# __git_heads accepts 0 or 1 arguments (to pass to __gitdir)
+# Generates completion reply with compgen from newline-separated possible
+# completion words by appending a space to all of them.
+# It accepts 1 to 4 arguments:
+# 1: List of possible completion words, separated by a single newline.
+# 2: A prefix to be added to each possible completion word (optional).
+# 3: Generate possible completion matches for this word (optional).
+# 4: A suffix to be appended to each possible completion word instead of
+#    the default space (optional).  If specified but empty, nothing is
+#    appended.
+__gitcomp_nl ()
+{
+	local s=$'\n' IFS=' '$'\t'$'\n'
+	local cur_="$cur" suffix=" "
+
+	if [ $# -gt 2 ]; then
+		cur_="$3"
+		if [ $# -gt 3 ]; then
+			suffix="$4"
+		fi
+	fi
+
+	IFS=$s
+	COMPREPLY=($(compgen -P "${2-}" -S "$suffix" -W "$1" -- "$cur_"))
+}
+
 __git_heads ()
 {
-	local cmd i is_hash=y dir="$(__gitdir "${1-}")"
+	local dir="$(__gitdir)"
 	if [ -d "$dir" ]; then
 		git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
 			refs/heads
 		return
 	fi
-	for i in $(git ls-remote "${1-}" 2>/dev/null); do
-		case "$is_hash,$i" in
-		y,*) is_hash=n ;;
-		n,*^{}) is_hash=y ;;
-		n,refs/heads/*) is_hash=y; echo "${i#refs/heads/}" ;;
-		n,*) is_hash=y; echo "$i" ;;
-		esac
-	done
 }
 
-# __git_tags accepts 0 or 1 arguments (to pass to __gitdir)
 __git_tags ()
 {
-	local cmd i is_hash=y dir="$(__gitdir "${1-}")"
+	local dir="$(__gitdir)"
 	if [ -d "$dir" ]; then
 		git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
 			refs/tags
 		return
 	fi
-	for i in $(git ls-remote "${1-}" 2>/dev/null); do
-		case "$is_hash,$i" in
-		y,*) is_hash=n ;;
-		n,*^{}) is_hash=y ;;
-		n,refs/tags/*) is_hash=y; echo "${i#refs/tags/}" ;;
-		n,*) is_hash=y; echo "$i" ;;
-		esac
-	done
 }
 
-# __git_refs accepts 0 or 1 arguments (to pass to __gitdir)
+# __git_refs accepts 0, 1 (to pass to __gitdir), or 2 arguments
+# presence of 2nd argument means use the guess heuristic employed
+# by checkout for tracking branches
 __git_refs ()
 {
-	local i is_hash=y dir="$(__gitdir "${1-}")"
-	local cur="${COMP_WORDS[COMP_CWORD]}" format refs
+	local i hash dir="$(__gitdir "${1-}")" track="${2-}"
+	local format refs
 	if [ -d "$dir" ]; then
 		case "$cur" in
 		refs|refs/*)
 			format="refname"
 			refs="${cur%/*}"
+			track=""
 			;;
 		*)
-			if [ -e "$dir/HEAD" ]; then echo HEAD; fi
+			for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD; do
+				if [ -e "$dir/$i" ]; then echo $i; fi
+			done
 			format="refname:short"
 			refs="refs/tags refs/heads refs/remotes"
 			;;
 		esac
 		git --git-dir="$dir" for-each-ref --format="%($format)" \
 			$refs
+		if [ -n "$track" ]; then
+			# employ the heuristic used by git checkout
+			# Try to find a remote branch that matches the completion word
+			# but only output if the branch name is unique
+			local ref entry
+			git --git-dir="$dir" for-each-ref --shell --format="ref=%(refname:short)" \
+				"refs/remotes/" | \
+			while read -r entry; do
+				eval "$entry"
+				ref="${ref#*/}"
+				if [[ "$ref" == "$cur"* ]]; then
+					echo "$ref"
+				fi
+			done | uniq -u
+		fi
 		return
 	fi
-	for i in $(git ls-remote "$dir" 2>/dev/null); do
-		case "$is_hash,$i" in
-		y,*) is_hash=n ;;
-		n,*^{}) is_hash=y ;;
-		n,refs/tags/*) is_hash=y; echo "${i#refs/tags/}" ;;
-		n,refs/heads/*) is_hash=y; echo "${i#refs/heads/}" ;;
-		n,refs/remotes/*) is_hash=y; echo "${i#refs/remotes/}" ;;
-		n,*) is_hash=y; echo "$i" ;;
-		esac
-	done
+	case "$cur" in
+	refs|refs/*)
+		git ls-remote "$dir" "$cur*" 2>/dev/null | \
+		while read -r hash i; do
+			case "$i" in
+			*^{}) ;;
+			*) echo "$i" ;;
+			esac
+		done
+		;;
+	*)
+		git ls-remote "$dir" HEAD ORIG_HEAD 'refs/tags/*' 'refs/heads/*' 'refs/remotes/*' 2>/dev/null | \
+		while read -r hash i; do
+			case "$i" in
+			*^{}) ;;
+			refs/*) echo "${i#refs/*/}" ;;
+			*) echo "$i" ;;
+			esac
+		done
+		;;
+	esac
 }
 
 # __git_refs2 requires 1 argument (to pass to __git_refs)
@@ -263,46 +634,30 @@
 # __git_refs_remotes requires 1 argument (to pass to ls-remote)
 __git_refs_remotes ()
 {
-	local cmd i is_hash=y
-	for i in $(git ls-remote "$1" 2>/dev/null); do
-		case "$is_hash,$i" in
-		n,refs/heads/*)
-			is_hash=y
-			echo "$i:refs/remotes/$1/${i#refs/heads/}"
-			;;
-		y,*) is_hash=n ;;
-		n,*^{}) is_hash=y ;;
-		n,refs/tags/*) is_hash=y;;
-		n,*) is_hash=y; ;;
-		esac
+	local i hash
+	git ls-remote "$1" 'refs/heads/*' 2>/dev/null | \
+	while read -r hash i; do
+		echo "$i:refs/remotes/$1/${i#refs/heads/}"
 	done
 }
 
 __git_remotes ()
 {
 	local i ngoff IFS=$'\n' d="$(__gitdir)"
-	shopt -q nullglob || ngoff=1
-	shopt -s nullglob
+	__git_shopt -q nullglob || ngoff=1
+	__git_shopt -s nullglob
 	for i in "$d/remotes"/*; do
 		echo ${i#$d/remotes/}
 	done
-	[ "$ngoff" ] && shopt -u nullglob
-	for i in $(git --git-dir="$d" config --list); do
-		case "$i" in
-		remote.*.url=*)
-			i="${i#remote.}"
-			echo "${i/.url=*/}"
-			;;
-		esac
+	[ "$ngoff" ] && __git_shopt -u nullglob
+	for i in $(git --git-dir="$d" config --get-regexp 'remote\..*\.url' 2>/dev/null); do
+		i="${i#remote.}"
+		echo "${i/.url*/}"
 	done
 }
 
-__git_merge_strategies ()
+__git_list_merge_strategies ()
 {
-	if [ -n "$__git_merge_strategylist" ]; then
-		echo "$__git_merge_strategylist"
-		return
-	fi
 	git merge -s help 2>&1 |
 	sed -n -e '/[Aa]vailable strategies are: /,/^$/{
 		s/\.$//
@@ -312,27 +667,39 @@
 		p
 	}'
 }
-__git_merge_strategylist=
-__git_merge_strategylist=$(__git_merge_strategies 2>/dev/null)
 
-__git_complete_file ()
+__git_merge_strategies=
+# 'git merge -s help' (and thus detection of the merge strategy
+# list) fails, unfortunately, if run outside of any git working
+# tree.  __git_merge_strategies is set to the empty string in
+# that case, and the detection will be repeated the next time it
+# is needed.
+__git_compute_merge_strategies ()
 {
-	local pfx ls ref cur="${COMP_WORDS[COMP_CWORD]}"
-	case "$cur" in
+	: ${__git_merge_strategies:=$(__git_list_merge_strategies)}
+}
+
+__git_complete_revlist_file ()
+{
+	local pfx ls ref cur_="$cur"
+	case "$cur_" in
+	*..?*:*)
+		return
+		;;
 	?*:*)
-		ref="${cur%%:*}"
-		cur="${cur#*:}"
-		case "$cur" in
+		ref="${cur_%%:*}"
+		cur_="${cur_#*:}"
+		case "$cur_" in
 		?*/*)
-			pfx="${cur%/*}"
-			cur="${cur##*/}"
+			pfx="${cur_%/*}"
+			cur_="${cur_##*/}"
 			ls="$ref:$pfx"
 			pfx="$pfx/"
 			;;
 		*)
 			ls="$ref"
 			;;
-	    esac
+		esac
 
 		case "$COMP_WORDBREAKS" in
 		*:*) : great ;;
@@ -355,42 +722,127 @@
 				           s,$,/,
 				       }
 				       s/^.*	//')" \
-			-- "$cur"))
+			-- "$cur_"))
+		;;
+	*...*)
+		pfx="${cur_%...*}..."
+		cur_="${cur_#*...}"
+		__gitcomp_nl "$(__git_refs)" "$pfx" "$cur_"
+		;;
+	*..*)
+		pfx="${cur_%..*}.."
+		cur_="${cur_#*..}"
+		__gitcomp_nl "$(__git_refs)" "$pfx" "$cur_"
 		;;
 	*)
-		__gitcomp "$(__git_refs)"
+		__gitcomp_nl "$(__git_refs)"
 		;;
 	esac
 }
 
+
+__git_complete_file ()
+{
+	__git_complete_revlist_file
+}
+
 __git_complete_revlist ()
 {
-	local pfx cur="${COMP_WORDS[COMP_CWORD]}"
-	case "$cur" in
-	*...*)
-		pfx="${cur%...*}..."
-		cur="${cur#*...}"
-		__gitcomp "$(__git_refs)" "$pfx" "$cur"
+	__git_complete_revlist_file
+}
+
+__git_complete_remote_or_refspec ()
+{
+	local cur_="$cur" cmd="${words[1]}"
+	local i c=2 remote="" pfx="" lhs=1 no_complete_refspec=0
+	while [ $c -lt $cword ]; do
+		i="${words[c]}"
+		case "$i" in
+		--mirror) [ "$cmd" = "push" ] && no_complete_refspec=1 ;;
+		--all)
+			case "$cmd" in
+			push) no_complete_refspec=1 ;;
+			fetch)
+				COMPREPLY=()
+				return
+				;;
+			*) ;;
+			esac
+			;;
+		-*) ;;
+		*) remote="$i"; break ;;
+		esac
+		c=$((++c))
+	done
+	if [ -z "$remote" ]; then
+		__gitcomp_nl "$(__git_remotes)"
+		return
+	fi
+	if [ $no_complete_refspec = 1 ]; then
+		COMPREPLY=()
+		return
+	fi
+	[ "$remote" = "." ] && remote=
+	case "$cur_" in
+	*:*)
+		case "$COMP_WORDBREAKS" in
+		*:*) : great ;;
+		*)   pfx="${cur_%%:*}:" ;;
+		esac
+		cur_="${cur_#*:}"
+		lhs=0
 		;;
-	*..*)
-		pfx="${cur%..*}.."
-		cur="${cur#*..}"
-		__gitcomp "$(__git_refs)" "$pfx" "$cur"
+	+*)
+		pfx="+"
+		cur_="${cur_#+}"
 		;;
-	*)
-		__gitcomp "$(__git_refs)"
+	esac
+	case "$cmd" in
+	fetch)
+		if [ $lhs = 1 ]; then
+			__gitcomp_nl "$(__git_refs2 "$remote")" "$pfx" "$cur_"
+		else
+			__gitcomp_nl "$(__git_refs)" "$pfx" "$cur_"
+		fi
+		;;
+	pull)
+		if [ $lhs = 1 ]; then
+			__gitcomp_nl "$(__git_refs "$remote")" "$pfx" "$cur_"
+		else
+			__gitcomp_nl "$(__git_refs)" "$pfx" "$cur_"
+		fi
+		;;
+	push)
+		if [ $lhs = 1 ]; then
+			__gitcomp_nl "$(__git_refs)" "$pfx" "$cur_"
+		else
+			__gitcomp_nl "$(__git_refs "$remote")" "$pfx" "$cur_"
+		fi
 		;;
 	esac
 }
 
-__git_all_commands ()
+__git_complete_strategy ()
 {
-	if [ -n "$__git_all_commandlist" ]; then
-		echo "$__git_all_commandlist"
-		return
-	fi
+	__git_compute_merge_strategies
+	case "$prev" in
+	-s|--strategy)
+		__gitcomp "$__git_merge_strategies"
+		return 0
+	esac
+	case "$cur" in
+	--strategy=*)
+		__gitcomp "$__git_merge_strategies" "" "${cur##--strategy=}"
+		return 0
+		;;
+	esac
+	return 1
+}
+
+__git_list_all_commands ()
+{
 	local i IFS=" "$'\n'
-	for i in $(git help -a|egrep '^ ')
+	for i in $(git help -a|egrep '^  [a-zA-Z0-9]')
 	do
 		case $i in
 		*--*)             : helper pattern;;
@@ -398,17 +850,18 @@
 		esac
 	done
 }
-__git_all_commandlist=
-__git_all_commandlist="$(__git_all_commands 2>/dev/null)"
 
-__git_porcelain_commands ()
+__git_all_commands=
+__git_compute_all_commands ()
 {
-	if [ -n "$__git_porcelain_commandlist" ]; then
-		echo "$__git_porcelain_commandlist"
-		return
-	fi
+	: ${__git_all_commands:=$(__git_list_all_commands)}
+}
+
+__git_list_porcelain_commands ()
+{
 	local i IFS=" "$'\n'
-	for i in "help" $(__git_all_commands)
+	__git_compute_all_commands
+	for i in "help" $__git_all_commands
 	do
 		case $i in
 		*--*)             : helper pattern;;
@@ -459,7 +912,7 @@
 		quiltimport)      : import;;
 		read-tree)        : plumbing;;
 		receive-pack)     : plumbing;;
-		reflog)           : plumbing;;
+		remote-*)         : transport;;
 		repo-config)      : deprecated;;
 		rerere)           : plumbing;;
 		rev-list)         : plumbing;;
@@ -489,17 +942,35 @@
 		esac
 	done
 }
-__git_porcelain_commandlist=
-__git_porcelain_commandlist="$(__git_porcelain_commands 2>/dev/null)"
+
+__git_porcelain_commands=
+__git_compute_porcelain_commands ()
+{
+	__git_compute_all_commands
+	: ${__git_porcelain_commands:=$(__git_list_porcelain_commands)}
+}
+
+__git_pretty_aliases ()
+{
+	local i IFS=$'\n'
+	for i in $(git --git-dir="$(__gitdir)" config --get-regexp "pretty\..*" 2>/dev/null); do
+		case "$i" in
+		pretty.*)
+			i="${i#pretty.}"
+			echo "${i/ */}"
+			;;
+		esac
+	done
+}
 
 __git_aliases ()
 {
 	local i IFS=$'\n'
-	for i in $(git --git-dir="$(__gitdir)" config --list); do
+	for i in $(git --git-dir="$(__gitdir)" config --get-regexp "alias\..*" 2>/dev/null); do
 		case "$i" in
 		alias.*)
 			i="${i#alias.}"
-			echo "${i/=*/}"
+			echo "${i/ */}"
 			;;
 		esac
 	done
@@ -511,20 +982,28 @@
 	local word cmdline=$(git --git-dir="$(__gitdir)" \
 		config --get "alias.$1")
 	for word in $cmdline; do
-		if [ "${word##-*}" ]; then
-			echo $word
+		case "$word" in
+		\!gitk|gitk)
+			echo "gitk"
 			return
-		fi
+			;;
+		\!*)	: shell command alias ;;
+		-*)	: option ;;
+		*=*)	: setting env ;;
+		git)	: git itself ;;
+		*)
+			echo "$word"
+			return
+		esac
 	done
 }
 
-# __git_find_subcommand requires 1 argument
-__git_find_subcommand ()
+# __git_find_on_cmdline requires 1 argument
+__git_find_on_cmdline ()
 {
 	local word subcommand c=1
-
-	while [ $c -lt $COMP_CWORD ]; do
-		word="${COMP_WORDS[c]}"
+	while [ $c -lt $cword ]; do
+		word="${words[c]}"
 		for subcommand in $1; do
 			if [ "$subcommand" = "$word" ]; then
 				echo "$subcommand"
@@ -538,8 +1017,8 @@
 __git_has_doubledash ()
 {
 	local c=1
-	while [ $c -lt $COMP_CWORD ]; do
-		if [ "--" = "${COMP_WORDS[c]}" ]; then
+	while [ $c -lt $cword ]; do
+		if [ "--" = "${words[c]}" ]; then
 			return 0
 		fi
 		c=$((++c))
@@ -551,9 +1030,9 @@
 
 _git_am ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}" dir="$(__gitdir)"
+	local dir="$(__gitdir)"
 	if [ -d "$dir"/rebase-apply ]; then
-		__gitcomp "--skip --resolved --abort"
+		__gitcomp "--skip --continue --resolved --abort"
 		return
 	fi
 	case "$cur" in
@@ -563,8 +1042,10 @@
 		;;
 	--*)
 		__gitcomp "
-			--signoff --utf8 --binary --3way --interactive
-			--whitespace=
+			--3way --committer-date-is-author-date --ignore-date
+			--ignore-whitespace --ignore-space-change
+			--interactive --keep --no-utf8 --signoff --utf8
+			--whitespace= --scissors
 			"
 		return
 	esac
@@ -573,7 +1054,6 @@
 
 _git_apply ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--whitespace=*)
 		__gitcomp "$__git_whitespacelist" "" "${cur##--whitespace=}"
@@ -584,6 +1064,7 @@
 			--stat --numstat --summary --check --index
 			--cached --index-info --reverse --reject --unidiff-zero
 			--apply --no-add --exclude=
+			--ignore-whitespace --ignore-space-change
 			--whitespace= --inaccurate-eof --verbose
 			"
 		return
@@ -595,7 +1076,6 @@
 {
 	__git_has_doubledash && return
 
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
 		__gitcomp "
@@ -609,14 +1089,13 @@
 
 _git_archive ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--format=*)
 		__gitcomp "$(git archive --list)" "" "${cur##--format=}"
 		return
 		;;
 	--remote=*)
-		__gitcomp "$(__git_remotes)" "" "${cur##--remote=}"
+		__gitcomp_nl "$(__git_remotes)" "" "${cur##--remote=}"
 		return
 		;;
 	--*)
@@ -635,15 +1114,19 @@
 	__git_has_doubledash && return
 
 	local subcommands="start bad good skip reset visualize replay log run"
-	local subcommand="$(__git_find_subcommand "$subcommands")"
+	local subcommand="$(__git_find_on_cmdline "$subcommands")"
 	if [ -z "$subcommand" ]; then
-		__gitcomp "$subcommands"
+		if [ -f "$(__gitdir)"/BISECT_START ]; then
+			__gitcomp "$subcommands"
+		else
+			__gitcomp "replay start"
+		fi
 		return
 	fi
 
 	case "$subcommand" in
-	bad|good|reset|skip)
-		__gitcomp "$(__git_refs)"
+	bad|good|reset|skip|start)
+		__gitcomp_nl "$(__git_refs)"
 		;;
 	*)
 		COMPREPLY=()
@@ -655,8 +1138,8 @@
 {
 	local i c=1 only_local_ref="n" has_r="n"
 
-	while [ $c -lt $COMP_CWORD ]; do
-		i="${COMP_WORDS[c]}"
+	while [ $c -lt $cword ]; do
+		i="${words[c]}"
 		case "$i" in
 		-d|-m)	only_local_ref="y" ;;
 		-r)	has_r="y" ;;
@@ -664,18 +1147,19 @@
 		c=$((++c))
 	done
 
-	case "${COMP_WORDS[COMP_CWORD]}" in
+	case "$cur" in
 	--*)
 		__gitcomp "
 			--color --no-color --verbose --abbrev= --no-abbrev
 			--track --no-track --contains --merged --no-merged
+			--set-upstream
 			"
 		;;
 	*)
 		if [ $only_local_ref = "y" -a $has_r = "n" ]; then
-			__gitcomp "$(__git_heads)"
+			__gitcomp_nl "$(__git_heads)"
 		else
-			__gitcomp "$(__git_refs)"
+			__gitcomp_nl "$(__git_refs)"
 		fi
 		;;
 	esac
@@ -683,8 +1167,8 @@
 
 _git_bundle ()
 {
-	local cmd="${COMP_WORDS[2]}"
-	case "$COMP_CWORD" in
+	local cmd="${words[2]}"
+	case "$cword" in
 	2)
 		__gitcomp "create list-heads verify unbundle"
 		;;
@@ -705,7 +1189,26 @@
 {
 	__git_has_doubledash && return
 
-	__gitcomp "$(__git_refs)"
+	case "$cur" in
+	--conflict=*)
+		__gitcomp "diff3 merge" "" "${cur##--conflict=}"
+		;;
+	--*)
+		__gitcomp "
+			--quiet --ours --theirs --track --no-track --merge
+			--conflict= --orphan --patch
+			"
+		;;
+	*)
+		# check if --track, --no-track, or --no-guess was specified
+		# if so, disable DWIM mode
+		local flags="--track --no-track --no-guess" track=1
+		if [ -n "$(__git_find_on_cmdline "$flags")" ]; then
+			track=''
+		fi
+		__gitcomp_nl "$(__git_refs '' $track)"
+		;;
+	esac
 }
 
 _git_cherry ()
@@ -715,13 +1218,12 @@
 
 _git_cherry_pick ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
 		__gitcomp "--edit --no-commit"
 		;;
 	*)
-		__gitcomp "$(__git_refs)"
+		__gitcomp_nl "$(__git_refs)"
 		;;
 	esac
 }
@@ -730,7 +1232,6 @@
 {
 	__git_has_doubledash && return
 
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
 		__gitcomp "--dry-run --quiet"
@@ -742,7 +1243,6 @@
 
 _git_clone ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
 		__gitcomp "
@@ -769,12 +1269,29 @@
 {
 	__git_has_doubledash && return
 
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
+	--cleanup=*)
+		__gitcomp "default strip verbatim whitespace
+			" "" "${cur##--cleanup=}"
+		return
+		;;
+	--reuse-message=*|--reedit-message=*|\
+	--fixup=*|--squash=*)
+		__gitcomp_nl "$(__git_refs)" "" "${cur#*=}"
+		return
+		;;
+	--untracked-files=*)
+		__gitcomp "all no normal" "" "${cur##--untracked-files=}"
+		return
+		;;
 	--*)
 		__gitcomp "
 			--all --author= --signoff --verify --no-verify
 			--edit --amend --include --only --interactive
+			--dry-run --reuse-message= --reedit-message=
+			--reset-author --file= --message= --template=
+			--cleanup= --untracked-files --untracked-files=
+			--verbose --quiet --fixup= --squash=
 			"
 		return
 	esac
@@ -783,7 +1300,6 @@
 
 _git_describe ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
 		__gitcomp "
@@ -792,7 +1308,7 @@
 			"
 		return
 	esac
-	__gitcomp "$(__git_refs)"
+	__gitcomp_nl "$(__git_refs)"
 }
 
 __git_diff_common_options="--stat --numstat --shortstat --summary
@@ -807,61 +1323,85 @@
 			--inter-hunk-context=
 			--patience
 			--raw
+			--dirstat --dirstat= --dirstat-by-file
+			--dirstat-by-file= --cumulative
 "
 
 _git_diff ()
 {
 	__git_has_doubledash && return
 
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
-		__gitcomp "--cached --pickaxe-all --pickaxe-regex
-			--base --ours --theirs
+		__gitcomp "--cached --staged --pickaxe-all --pickaxe-regex
+			--base --ours --theirs --no-index
 			$__git_diff_common_options
 			"
 		return
 		;;
 	esac
+	__git_complete_revlist_file
+}
+
+__git_mergetools_common="diffuse ecmerge emerge kdiff3 meld opendiff
+			tkdiff vimdiff gvimdiff xxdiff araxis p4merge bc3
+"
+
+_git_difftool ()
+{
+	__git_has_doubledash && return
+
+	case "$cur" in
+	--tool=*)
+		__gitcomp "$__git_mergetools_common kompare" "" "${cur##--tool=}"
+		return
+		;;
+	--*)
+		__gitcomp "--cached --staged --pickaxe-all --pickaxe-regex
+			--base --ours --theirs
+			--no-renames --diff-filter= --find-copies-harder
+			--relative --ignore-submodules
+			--tool="
+		return
+		;;
+	esac
 	__git_complete_file
 }
 
+__git_fetch_options="
+	--quiet --verbose --append --upload-pack --force --keep --depth=
+	--tags --no-tags --all --prune --dry-run
+"
+
 _git_fetch ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
-
-	if [ "$COMP_CWORD" = 2 ]; then
-		__gitcomp "$(__git_remotes)"
-	else
-		case "$cur" in
-		*:*)
-			local pfx=""
-			case "$COMP_WORDBREAKS" in
-			*:*) : great ;;
-			*)   pfx="${cur%%:*}:" ;;
-			esac
-			__gitcomp "$(__git_refs)" "$pfx" "${cur#*:}"
-			;;
-		*)
-			__gitcomp "$(__git_refs2 "${COMP_WORDS[2]}")"
-			;;
-		esac
-	fi
+	case "$cur" in
+	--*)
+		__gitcomp "$__git_fetch_options"
+		return
+		;;
+	esac
+	__git_complete_remote_or_refspec
 }
 
 _git_format_patch ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
+	--thread=*)
+		__gitcomp "
+			deep shallow
+			" "" "${cur##--thread=}"
+		return
+		;;
 	--*)
 		__gitcomp "
-			--stdout --attach --thread
+			--stdout --attach --no-attach --thread --thread=
 			--output-directory
 			--numbered --start-number
 			--numbered-files
 			--keep-subject
-			--signoff
-			--in-reply-to=
+			--signoff --signature --no-signature
+			--in-reply-to= --cc=
 			--full-index --binary
 			--not --all
 			--cover-letter
@@ -875,9 +1415,22 @@
 	__git_complete_revlist
 }
 
+_git_fsck ()
+{
+	case "$cur" in
+	--*)
+		__gitcomp "
+			--tags --root --unreachable --cache --no-reflogs --full
+			--strict --verbose --lost-found
+			"
+		return
+		;;
+	esac
+	COMPREPLY=()
+}
+
 _git_gc ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
 		__gitcomp "--prune --aggressive"
@@ -887,49 +1440,68 @@
 	COMPREPLY=()
 }
 
+_git_gitk ()
+{
+	_gitk
+}
+
+__git_match_ctag() {
+	awk "/^${1////\\/}/ { print \$1 }" "$2"
+}
+
 _git_grep ()
 {
 	__git_has_doubledash && return
 
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
 		__gitcomp "
 			--cached
 			--text --ignore-case --word-regexp --invert-match
-			--full-name
+			--full-name --line-number
 			--extended-regexp --basic-regexp --fixed-strings
+			--perl-regexp
 			--files-with-matches --name-only
 			--files-without-match
+			--max-depth
 			--count
 			--and --or --not --all-match
 			"
 		return
 		;;
 	esac
-	COMPREPLY=()
+
+	case "$cword,$prev" in
+	2,*|*,-*)
+		if test -r tags; then
+			__gitcomp_nl "$(__git_match_ctag "$cur" tags)"
+			return
+		fi
+		;;
+	esac
+
+	__gitcomp_nl "$(__git_refs)"
 }
 
 _git_help ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
 		__gitcomp "--all --info --man --web"
 		return
 		;;
 	esac
-	__gitcomp "$(__git_all_commands)
+	__git_compute_all_commands
+	__gitcomp "$__git_all_commands $(__git_aliases)
 		attributes cli core-tutorial cvs-migration
 		diffcore gitk glossary hooks ignore modules
-		repository-layout tutorial tutorial-2
+		namespaces repository-layout tutorial tutorial-2
 		workflows
 		"
 }
 
 _git_init ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--shared=*)
 		__gitcomp "
@@ -949,7 +1521,6 @@
 {
 	__git_has_doubledash && return
 
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
 		__gitcomp "--cached --deleted --modified --others --ignored
@@ -967,7 +1538,7 @@
 
 _git_ls_remote ()
 {
-	__gitcomp "$(__git_remotes)"
+	__gitcomp_nl "$(__git_remotes)"
 }
 
 _git_ls_tree ()
@@ -975,43 +1546,71 @@
 	__git_complete_file
 }
 
+# Options that go well for log, shortlog and gitk
+__git_log_common_options="
+	--not --all
+	--branches --tags --remotes
+	--first-parent --merges --no-merges
+	--max-count=
+	--max-age= --since= --after=
+	--min-age= --until= --before=
+	--min-parents= --max-parents=
+	--no-min-parents --no-max-parents
+"
+# Options that go well for log and gitk (not shortlog)
+__git_log_gitk_options="
+	--dense --sparse --full-history
+	--simplify-merges --simplify-by-decoration
+	--left-right --notes --no-notes
+"
+# Options that go well for log and shortlog (not gitk)
+__git_log_shortlog_options="
+	--author= --committer= --grep=
+	--all-match
+"
+
 __git_log_pretty_formats="oneline short medium full fuller email raw format:"
+__git_log_date_formats="relative iso8601 rfc2822 short local default raw"
 
 _git_log ()
 {
 	__git_has_doubledash && return
 
-	local cur="${COMP_WORDS[COMP_CWORD]}"
+	local g="$(git rev-parse --git-dir 2>/dev/null)"
+	local merge=""
+	if [ -f "$g/MERGE_HEAD" ]; then
+		merge="--merge"
+	fi
 	case "$cur" in
-	--pretty=*)
-		__gitcomp "$__git_log_pretty_formats
-			" "" "${cur##--pretty=}"
+	--pretty=*|--format=*)
+		__gitcomp "$__git_log_pretty_formats $(__git_pretty_aliases)
+			" "" "${cur#*=}"
 		return
 		;;
 	--date=*)
-		__gitcomp "
-			relative iso8601 rfc2822 short local default
-		" "" "${cur##--date=}"
+		__gitcomp "$__git_log_date_formats" "" "${cur##--date=}"
+		return
+		;;
+	--decorate=*)
+		__gitcomp "long short" "" "${cur##--decorate=}"
 		return
 		;;
 	--*)
 		__gitcomp "
-			--max-count= --max-age= --since= --after=
-			--min-age= --before= --until=
+			$__git_log_common_options
+			$__git_log_shortlog_options
+			$__git_log_gitk_options
 			--root --topo-order --date-order --reverse
-			--no-merges --follow
+			--follow --full-diff
 			--abbrev-commit --abbrev=
 			--relative-date --date=
-			--author= --committer= --grep=
-			--all-match
-			--pretty=
-			--not --all
-			--left-right --cherry-pick
+			--pretty= --format= --oneline
+			--cherry-pick
 			--graph
-			--decorate
+			--decorate --decorate=
 			--walk-reflogs
-			--parents --children --full-history
-			--merge
+			--parents --children
+			$merge
 			$__git_diff_common_options
 			--pickaxe-all --pickaxe-regex
 			"
@@ -1021,37 +1620,28 @@
 	__git_complete_revlist
 }
 
+__git_merge_options="
+	--no-commit --no-stat --log --no-log --squash --strategy
+	--commit --stat --no-squash --ff --no-ff --ff-only
+"
+
 _git_merge ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
-	case "${COMP_WORDS[COMP_CWORD-1]}" in
-	-s|--strategy)
-		__gitcomp "$(__git_merge_strategies)"
-		return
-	esac
+	__git_complete_strategy && return
+
 	case "$cur" in
-	--strategy=*)
-		__gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}"
-		return
-		;;
 	--*)
-		__gitcomp "
-			--no-commit --no-stat --log --no-log --squash --strategy
-			"
+		__gitcomp "$__git_merge_options"
 		return
 	esac
-	__gitcomp "$(__git_refs)"
+	__gitcomp_nl "$(__git_refs)"
 }
 
 _git_mergetool ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--tool=*)
-		__gitcomp "
-			kdiff3 tkdiff meld xxdiff emerge
-			vimdiff gvimdiff ecmerge opendiff
-			" "" "${cur##--tool=}"
+		__gitcomp "$__git_mergetools_common tortoisemerge" "" "${cur##--tool=}"
 		return
 		;;
 	--*)
@@ -1064,12 +1654,11 @@
 
 _git_merge_base ()
 {
-	__gitcomp "$(__git_refs)"
+	__gitcomp_nl "$(__git_refs)"
 }
 
 _git_mv ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
 		__gitcomp "--dry-run"
@@ -1084,80 +1673,164 @@
 	__gitcomp "--tags --all --stdin"
 }
 
+_git_notes ()
+{
+	local subcommands='add append copy edit list prune remove show'
+	local subcommand="$(__git_find_on_cmdline "$subcommands")"
+
+	case "$subcommand,$cur" in
+	,--*)
+		__gitcomp '--ref'
+		;;
+	,*)
+		case "${words[cword-1]}" in
+		--ref)
+			__gitcomp_nl "$(__git_refs)"
+			;;
+		*)
+			__gitcomp "$subcommands --ref"
+			;;
+		esac
+		;;
+	add,--reuse-message=*|append,--reuse-message=*|\
+	add,--reedit-message=*|append,--reedit-message=*)
+		__gitcomp_nl "$(__git_refs)" "" "${cur#*=}"
+		;;
+	add,--*|append,--*)
+		__gitcomp '--file= --message= --reedit-message=
+				--reuse-message='
+		;;
+	copy,--*)
+		__gitcomp '--stdin'
+		;;
+	prune,--*)
+		__gitcomp '--dry-run --verbose'
+		;;
+	prune,*)
+		;;
+	*)
+		case "${words[cword-1]}" in
+		-m|-F)
+			;;
+		*)
+			__gitcomp_nl "$(__git_refs)"
+			;;
+		esac
+		;;
+	esac
+}
+
 _git_pull ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
+	__git_complete_strategy && return
 
-	if [ "$COMP_CWORD" = 2 ]; then
-		__gitcomp "$(__git_remotes)"
-	else
-		__gitcomp "$(__git_refs "${COMP_WORDS[2]}")"
-	fi
+	case "$cur" in
+	--*)
+		__gitcomp "
+			--rebase --no-rebase
+			$__git_merge_options
+			$__git_fetch_options
+		"
+		return
+		;;
+	esac
+	__git_complete_remote_or_refspec
 }
 
 _git_push ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
-
-	if [ "$COMP_CWORD" = 2 ]; then
-		__gitcomp "$(__git_remotes)"
-	else
-		case "$cur" in
-		*:*)
-			local pfx=""
-			case "$COMP_WORDBREAKS" in
-			*:*) : great ;;
-			*)   pfx="${cur%%:*}:" ;;
-			esac
-
-			__gitcomp "$(__git_refs "${COMP_WORDS[2]}")" "$pfx" "${cur#*:}"
-			;;
-		+*)
-			__gitcomp "$(__git_refs)" + "${cur#+}"
-			;;
-		*)
-			__gitcomp "$(__git_refs)"
-			;;
-		esac
-	fi
+	case "$prev" in
+	--repo)
+		__gitcomp_nl "$(__git_remotes)"
+		return
+	esac
+	case "$cur" in
+	--repo=*)
+		__gitcomp_nl "$(__git_remotes)" "" "${cur##--repo=}"
+		return
+		;;
+	--*)
+		__gitcomp "
+			--all --mirror --tags --dry-run --force --verbose
+			--receive-pack= --repo= --set-upstream
+		"
+		return
+		;;
+	esac
+	__git_complete_remote_or_refspec
 }
 
 _git_rebase ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}" dir="$(__gitdir)"
+	local dir="$(__gitdir)"
 	if [ -d "$dir"/rebase-apply ] || [ -d "$dir"/rebase-merge ]; then
 		__gitcomp "--continue --skip --abort"
 		return
 	fi
-	case "${COMP_WORDS[COMP_CWORD-1]}" in
-	-s|--strategy)
-		__gitcomp "$(__git_merge_strategies)"
-		return
-	esac
+	__git_complete_strategy && return
 	case "$cur" in
-	--strategy=*)
-		__gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}"
+	--whitespace=*)
+		__gitcomp "$__git_whitespacelist" "" "${cur##--whitespace=}"
 		return
 		;;
 	--*)
-		__gitcomp "--onto --merge --strategy --interactive"
+		__gitcomp "
+			--onto --merge --strategy --interactive
+			--preserve-merges --stat --no-stat
+			--committer-date-is-author-date --ignore-date
+			--ignore-whitespace --whitespace=
+			--autosquash
+			"
+
 		return
 	esac
-	__gitcomp "$(__git_refs)"
+	__gitcomp_nl "$(__git_refs)"
 }
 
+_git_reflog ()
+{
+	local subcommands="show delete expire"
+	local subcommand="$(__git_find_on_cmdline "$subcommands")"
+
+	if [ -z "$subcommand" ]; then
+		__gitcomp "$subcommands"
+	else
+		__gitcomp_nl "$(__git_refs)"
+	fi
+}
+
+__git_send_email_confirm_options="always never auto cc compose"
+__git_send_email_suppresscc_options="author self cc bodycc sob cccmd body all"
+
 _git_send_email ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
+	--confirm=*)
+		__gitcomp "
+			$__git_send_email_confirm_options
+			" "" "${cur##--confirm=}"
+		return
+		;;
+	--suppress-cc=*)
+		__gitcomp "
+			$__git_send_email_suppresscc_options
+			" "" "${cur##--suppress-cc=}"
+
+		return
+		;;
+	--smtp-encryption=*)
+		__gitcomp "ssl tls" "" "${cur##--smtp-encryption=}"
+		return
+		;;
 	--*)
-		__gitcomp "--bcc --cc --cc-cmd --chain-reply-to --compose
-			--dry-run --envelope-sender --from --identity
+		__gitcomp "--annotate --bcc --cc --cc-cmd --chain-reply-to
+			--compose --confirm= --dry-run --envelope-sender
+			--from --identity
 			--in-reply-to --no-chain-reply-to --no-signed-off-by-cc
 			--no-suppress-from --no-thread --quiet
 			--signed-off-by-cc --smtp-pass --smtp-server
-			--smtp-server-port --smtp-ssl --smtp-user --subject
-			--suppress-cc --suppress-from --thread --to
+			--smtp-server-port --smtp-encryption= --smtp-user
+			--subject --suppress-cc= --suppress-from --thread --to
 			--validate --no-validate"
 		return
 		;;
@@ -1165,41 +1838,84 @@
 	COMPREPLY=()
 }
 
+_git_stage ()
+{
+	_git_add
+}
+
+__git_config_get_set_variables ()
+{
+	local prevword word config_file= c=$cword
+	while [ $c -gt 1 ]; do
+		word="${words[c]}"
+		case "$word" in
+		--global|--system|--file=*)
+			config_file="$word"
+			break
+			;;
+		-f|--file)
+			config_file="$word $prevword"
+			break
+			;;
+		esac
+		prevword=$word
+		c=$((--c))
+	done
+
+	git --git-dir="$(__gitdir)" config $config_file --list 2>/dev/null |
+	while read -r line
+	do
+		case "$line" in
+		*.*=*)
+			echo "${line/=*/}"
+			;;
+		esac
+	done
+}
+
 _git_config ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
-	local prv="${COMP_WORDS[COMP_CWORD-1]}"
-	case "$prv" in
+	case "$prev" in
 	branch.*.remote)
-		__gitcomp "$(__git_remotes)"
+		__gitcomp_nl "$(__git_remotes)"
 		return
 		;;
 	branch.*.merge)
-		__gitcomp "$(__git_refs)"
+		__gitcomp_nl "$(__git_refs)"
 		return
 		;;
 	remote.*.fetch)
-		local remote="${prv#remote.}"
+		local remote="${prev#remote.}"
 		remote="${remote%.fetch}"
-		__gitcomp "$(__git_refs_remotes "$remote")"
+		if [ -z "$cur" ]; then
+			COMPREPLY=("refs/heads/")
+			return
+		fi
+		__gitcomp_nl "$(__git_refs_remotes "$remote")"
 		return
 		;;
 	remote.*.push)
-		local remote="${prv#remote.}"
+		local remote="${prev#remote.}"
 		remote="${remote%.push}"
-		__gitcomp "$(git --git-dir="$(__gitdir)" \
+		__gitcomp_nl "$(git --git-dir="$(__gitdir)" \
 			for-each-ref --format='%(refname):%(refname)' \
 			refs/heads)"
 		return
 		;;
 	pull.twohead|pull.octopus)
-		__gitcomp "$(__git_merge_strategies)"
+		__git_compute_merge_strategies
+		__gitcomp "$__git_merge_strategies"
 		return
 		;;
-	color.branch|color.diff|color.status)
+	color.branch|color.diff|color.interactive|\
+	color.showbranch|color.status|color.ui)
 		__gitcomp "always never auto"
 		return
 		;;
+	color.pager)
+		__gitcomp "false true"
+		return
+		;;
 	color.*.*)
 		__gitcomp "
 			normal black red green yellow blue magenta cyan white
@@ -1207,6 +1923,30 @@
 			"
 		return
 		;;
+	help.format)
+		__gitcomp "man info web html"
+		return
+		;;
+	log.date)
+		__gitcomp "$__git_log_date_formats"
+		return
+		;;
+	sendemail.aliasesfiletype)
+		__gitcomp "mutt mailrc pine elm gnus"
+		return
+		;;
+	sendemail.confirm)
+		__gitcomp "$__git_send_email_confirm_options"
+		return
+		;;
+	sendemail.suppresscc)
+		__gitcomp "$__git_send_email_suppresscc_options"
+		return
+		;;
+	--get|--get-all|--unset|--unset-all)
+		__gitcomp_nl "$(__git_config_get_set_variables)"
+		return
+		;;
 	*.*)
 		COMPREPLY=()
 		return
@@ -1224,56 +1964,113 @@
 		return
 		;;
 	branch.*.*)
-		local pfx="${cur%.*}."
-		cur="${cur##*.}"
-		__gitcomp "remote merge mergeoptions" "$pfx" "$cur"
+		local pfx="${cur%.*}." cur_="${cur##*.}"
+		__gitcomp "remote merge mergeoptions rebase" "$pfx" "$cur_"
 		return
 		;;
 	branch.*)
-		local pfx="${cur%.*}."
-		cur="${cur#*.}"
-		__gitcomp "$(__git_heads)" "$pfx" "$cur" "."
+		local pfx="${cur%.*}." cur_="${cur#*.}"
+		__gitcomp_nl "$(__git_heads)" "$pfx" "$cur_" "."
+		return
+		;;
+	guitool.*.*)
+		local pfx="${cur%.*}." cur_="${cur##*.}"
+		__gitcomp "
+			argprompt cmd confirm needsfile noconsole norescan
+			prompt revprompt revunmerged title
+			" "$pfx" "$cur_"
+		return
+		;;
+	difftool.*.*)
+		local pfx="${cur%.*}." cur_="${cur##*.}"
+		__gitcomp "cmd path" "$pfx" "$cur_"
+		return
+		;;
+	man.*.*)
+		local pfx="${cur%.*}." cur_="${cur##*.}"
+		__gitcomp "cmd path" "$pfx" "$cur_"
+		return
+		;;
+	mergetool.*.*)
+		local pfx="${cur%.*}." cur_="${cur##*.}"
+		__gitcomp "cmd path trustExitCode" "$pfx" "$cur_"
+		return
+		;;
+	pager.*)
+		local pfx="${cur%.*}." cur_="${cur#*.}"
+		__git_compute_all_commands
+		__gitcomp_nl "$__git_all_commands" "$pfx" "$cur_"
 		return
 		;;
 	remote.*.*)
-		local pfx="${cur%.*}."
-		cur="${cur##*.}"
+		local pfx="${cur%.*}." cur_="${cur##*.}"
 		__gitcomp "
 			url proxy fetch push mirror skipDefaultUpdate
-			receivepack uploadpack tagopt
-			" "$pfx" "$cur"
+			receivepack uploadpack tagopt pushurl
+			" "$pfx" "$cur_"
 		return
 		;;
 	remote.*)
-		local pfx="${cur%.*}."
-		cur="${cur#*.}"
-		__gitcomp "$(__git_remotes)" "$pfx" "$cur" "."
+		local pfx="${cur%.*}." cur_="${cur#*.}"
+		__gitcomp_nl "$(__git_remotes)" "$pfx" "$cur_" "."
+		return
+		;;
+	url.*.*)
+		local pfx="${cur%.*}." cur_="${cur##*.}"
+		__gitcomp "insteadOf pushInsteadOf" "$pfx" "$cur_"
 		return
 		;;
 	esac
 	__gitcomp "
+		add.ignoreErrors
+		advice.commitBeforeMerge
+		advice.detachedHead
+		advice.implicitIdentity
+		advice.pushNonFastForward
+		advice.resolveConflict
+		advice.statusHints
+		alias.
+		am.keepcr
+		apply.ignorewhitespace
 		apply.whitespace
 		branch.autosetupmerge
 		branch.autosetuprebase
+		browser.
 		clean.requireForce
 		color.branch
 		color.branch.current
 		color.branch.local
 		color.branch.plain
 		color.branch.remote
+		color.decorate.HEAD
+		color.decorate.branch
+		color.decorate.remoteBranch
+		color.decorate.stash
+		color.decorate.tag
 		color.diff
 		color.diff.commit
 		color.diff.frag
+		color.diff.func
 		color.diff.meta
 		color.diff.new
 		color.diff.old
 		color.diff.plain
 		color.diff.whitespace
+		color.grep
+		color.grep.context
+		color.grep.filename
+		color.grep.function
+		color.grep.linenumber
+		color.grep.match
+		color.grep.selected
+		color.grep.separator
 		color.interactive
+		color.interactive.error
 		color.interactive.header
 		color.interactive.help
 		color.interactive.prompt
 		color.pager
+		color.showbranch
 		color.status
 		color.status.added
 		color.status.changed
@@ -1282,20 +2079,29 @@
 		color.status.untracked
 		color.status.updated
 		color.ui
+		commit.status
 		commit.template
+		core.abbrev
+		core.askpass
+		core.attributesfile
 		core.autocrlf
 		core.bare
+		core.bigFileThreshold
 		core.compression
+		core.createObject
 		core.deltaBaseCacheLimit
 		core.editor
+		core.eol
 		core.excludesfile
 		core.fileMode
 		core.fsyncobjectfiles
 		core.gitProxy
 		core.ignoreCygwinFSTricks
 		core.ignoreStat
+		core.ignorecase
 		core.logAllRefUpdates
 		core.loosecompression
+		core.notesRef
 		core.packedGitLimit
 		core.packedGitWindowSize
 		core.pager
@@ -1305,6 +2111,7 @@
 		core.repositoryFormatVersion
 		core.safecrlf
 		core.sharedRepository
+		core.sparseCheckout
 		core.symlinks
 		core.trustctime
 		core.warnAmbiguousRefs
@@ -1312,15 +2119,30 @@
 		core.worktree
 		diff.autorefreshindex
 		diff.external
+		diff.ignoreSubmodules
 		diff.mnemonicprefix
+		diff.noprefix
 		diff.renameLimit
-		diff.renameLimit.
 		diff.renames
+		diff.suppressBlankEmpty
+		diff.tool
+		diff.wordRegex
+		difftool.
+		difftool.prompt
+		fetch.recurseSubmodules
 		fetch.unpackLimit
+		format.attach
+		format.cc
 		format.headers
 		format.numbered
 		format.pretty
+		format.signature
+		format.signoff
+		format.subjectprefix
 		format.suffix
+		format.thread
+		format.to
+		gc.
 		gc.aggressiveWindow
 		gc.auto
 		gc.autopacklimit
@@ -1331,6 +2153,7 @@
 		gc.rerereresolved
 		gc.rerereunresolved
 		gitcvs.allbinary
+		gitcvs.commitmsgannotation
 		gitcvs.dbTableNamePrefix
 		gitcvs.dbdriver
 		gitcvs.dbname
@@ -1339,6 +2162,7 @@
 		gitcvs.enabled
 		gitcvs.logfile
 		gitcvs.usecrlfattr
+		guitool.
 		gui.blamehistoryctx
 		gui.commitmsgwidth
 		gui.copyblamethreshold
@@ -1356,30 +2180,59 @@
 		http.lowSpeedLimit
 		http.lowSpeedTime
 		http.maxRequests
+		http.minSessions
 		http.noEPSV
+		http.postBuffer
 		http.proxy
 		http.sslCAInfo
 		http.sslCAPath
 		http.sslCert
+		http.sslCertPasswordProtected
 		http.sslKey
 		http.sslVerify
+		http.useragent
 		i18n.commitEncoding
 		i18n.logOutputEncoding
+		imap.authMethod
+		imap.folder
+		imap.host
+		imap.pass
+		imap.port
+		imap.preformattedHTML
+		imap.sslverify
+		imap.tunnel
+		imap.user
+		init.templatedir
 		instaweb.browser
 		instaweb.httpd
 		instaweb.local
 		instaweb.modulepath
 		instaweb.port
+		interactive.singlekey
 		log.date
+		log.decorate
 		log.showroot
+		mailmap.file
+		man.
 		man.viewer
+		merge.
 		merge.conflictstyle
 		merge.log
 		merge.renameLimit
+		merge.renormalize
 		merge.stat
 		merge.tool
 		merge.verbosity
+		mergetool.
 		mergetool.keepBackup
+		mergetool.keepTemporaries
+		mergetool.prompt
+		notes.displayRef
+		notes.rewrite.
+		notes.rewrite.amend
+		notes.rewrite.rebase
+		notes.rewriteMode
+		notes.rewriteRef
 		pack.compression
 		pack.deltaCacheLimit
 		pack.deltaCacheSize
@@ -1389,21 +2242,58 @@
 		pack.threads
 		pack.window
 		pack.windowMemory
+		pager.
+		pretty.
 		pull.octopus
 		pull.twohead
+		push.default
+		rebase.autosquash
+		rebase.stat
+		receive.autogc
 		receive.denyCurrentBranch
+		receive.denyDeleteCurrent
 		receive.denyDeletes
 		receive.denyNonFastForwards
 		receive.fsckObjects
 		receive.unpackLimit
+		receive.updateserverinfo
+		remotes.
 		repack.usedeltabaseoffset
 		rerere.autoupdate
 		rerere.enabled
+		sendemail.
+		sendemail.aliasesfile
+		sendemail.aliasfiletype
+		sendemail.bcc
+		sendemail.cc
+		sendemail.cccmd
+		sendemail.chainreplyto
+		sendemail.confirm
+		sendemail.envelopesender
+		sendemail.from
+		sendemail.identity
+		sendemail.multiedit
+		sendemail.signedoffbycc
+		sendemail.smtpdomain
+		sendemail.smtpencryption
+		sendemail.smtppass
+		sendemail.smtpserver
+		sendemail.smtpserveroption
+		sendemail.smtpserverport
+		sendemail.smtpuser
+		sendemail.suppresscc
+		sendemail.suppressfrom
+		sendemail.thread
+		sendemail.to
+		sendemail.validate
 		showbranch.default
 		status.relativePaths
 		status.showUntrackedFiles
+		status.submodulesummary
+		submodule.
 		tar.umask
 		transfer.unpackLimit
+		url.
 		user.email
 		user.name
 		user.signingkey
@@ -1414,8 +2304,8 @@
 
 _git_remote ()
 {
-	local subcommands="add rename rm show prune update"
-	local subcommand="$(__git_find_subcommand "$subcommands")"
+	local subcommands="add rename rm show prune update set-head"
+	local subcommand="$(__git_find_on_cmdline "$subcommands")"
 	if [ -z "$subcommand" ]; then
 		__gitcomp "$subcommands"
 		return
@@ -1423,17 +2313,13 @@
 
 	case "$subcommand" in
 	rename|rm|show|prune)
-		__gitcomp "$(__git_remotes)"
+		__gitcomp_nl "$(__git_remotes)"
 		;;
 	update)
 		local i c='' IFS=$'\n'
-		for i in $(git --git-dir="$(__gitdir)" config --list); do
-			case "$i" in
-			remotes.*)
-				i="${i#remotes.}"
-				c="$c ${i/=*/}"
-				;;
-			esac
+		for i in $(git --git-dir="$(__gitdir)" config --get-regexp "remotes\..*" 2>/dev/null); do
+			i="${i#remotes.}"
+			c="$c ${i/ */}"
 		done
 		__gitcomp "$c"
 		;;
@@ -1443,37 +2329,39 @@
 	esac
 }
 
+_git_replace ()
+{
+	__gitcomp_nl "$(__git_refs)"
+}
+
 _git_reset ()
 {
 	__git_has_doubledash && return
 
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
-		__gitcomp "--merge --mixed --hard --soft"
+		__gitcomp "--merge --mixed --hard --soft --patch"
 		return
 		;;
 	esac
-	__gitcomp "$(__git_refs)"
+	__gitcomp_nl "$(__git_refs)"
 }
 
 _git_revert ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
 		__gitcomp "--edit --mainline --no-edit --no-commit --signoff"
 		return
 		;;
 	esac
-	__gitcomp "$(__git_refs)"
+	__gitcomp_nl "$(__git_refs)"
 }
 
 _git_rm ()
 {
 	__git_has_doubledash && return
 
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
 		__gitcomp "--cached --dry-run --ignore-unmatch --quiet"
@@ -1487,16 +2375,11 @@
 {
 	__git_has_doubledash && return
 
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
 		__gitcomp "
-			--max-count= --max-age= --since= --after=
-			--min-age= --before= --until=
-			--no-merges
-			--author= --committer= --grep=
-			--all-match
-			--not --all
+			$__git_log_common_options
+			$__git_log_shortlog_options
 			--numbered --summary
 			"
 		return
@@ -1509,15 +2392,14 @@
 {
 	__git_has_doubledash && return
 
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
-	--pretty=*)
-		__gitcomp "$__git_log_pretty_formats
-			" "" "${cur##--pretty=}"
+	--pretty=*|--format=*)
+		__gitcomp "$__git_log_pretty_formats $(__git_pretty_aliases)
+			" "" "${cur#*=}"
 		return
 		;;
 	--*)
-		__gitcomp "--pretty=
+		__gitcomp "--pretty= --format= --abbrev-commit --oneline
 			$__git_diff_common_options
 			"
 		return
@@ -1528,13 +2410,13 @@
 
 _git_show_branch ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
 		__gitcomp "
 			--all --remotes --topo-order --current --more=
 			--list --independent --merge-base --no-name
-			--sha1-name --topics --reflog
+			--color --no-color
+			--sha1-name --sparse --topics --reflog
 			"
 		return
 		;;
@@ -1544,24 +2426,35 @@
 
 _git_stash ()
 {
+	local save_opts='--keep-index --no-keep-index --quiet --patch'
 	local subcommands='save list show apply clear drop pop create branch'
-	local subcommand="$(__git_find_subcommand "$subcommands")"
+	local subcommand="$(__git_find_on_cmdline "$subcommands")"
 	if [ -z "$subcommand" ]; then
-		__gitcomp "$subcommands"
+		case "$cur" in
+		--*)
+			__gitcomp "$save_opts"
+			;;
+		*)
+			if [ -z "$(__git_find_on_cmdline "$save_opts")" ]; then
+				__gitcomp "$subcommands"
+			else
+				COMPREPLY=()
+			fi
+			;;
+		esac
 	else
-		local cur="${COMP_WORDS[COMP_CWORD]}"
 		case "$subcommand,$cur" in
 		save,--*)
-			__gitcomp "--keep-index"
+			__gitcomp "$save_opts"
 			;;
-		apply,--*)
-			__gitcomp "--index"
+		apply,--*|pop,--*)
+			__gitcomp "--index --quiet"
 			;;
-		show,--*|drop,--*|pop,--*|branch,--*)
+		show,--*|drop,--*|branch,--*)
 			COMPREPLY=()
 			;;
 		show,*|apply,*|drop,*|pop,*|branch,*)
-			__gitcomp "$(git --git-dir="$(__gitdir)" stash list \
+			__gitcomp_nl "$(git --git-dir="$(__gitdir)" stash list \
 					| sed -n -e 's/:.*//p')"
 			;;
 		*)
@@ -1576,8 +2469,7 @@
 	__git_has_doubledash && return
 
 	local subcommands="add status init update summary foreach sync"
-	if [ -z "$(__git_find_subcommand "$subcommands")" ]; then
-		local cur="${COMP_WORDS[COMP_CWORD]}"
+	if [ -z "$(__git_find_on_cmdline "$subcommands")" ]; then
 		case "$cur" in
 		--*)
 			__gitcomp "--quiet --cached"
@@ -1595,9 +2487,10 @@
 	local subcommands="
 		init fetch clone rebase dcommit log find-rev
 		set-tree commit-diff info create-ignore propget
-		proplist show-ignore show-externals
+		proplist show-ignore show-externals branch tag blame
+		migrate mkdirs reset gc
 		"
-	local subcommand="$(__git_find_subcommand "$subcommands")"
+	local subcommand="$(__git_find_on_cmdline "$subcommands")"
 	if [ -z "$subcommand" ]; then
 		__gitcomp "$subcommands"
 	else
@@ -1606,19 +2499,20 @@
 			--follow-parent --authors-file= --repack=
 			--no-metadata --use-svm-props --use-svnsync-props
 			--log-window-size= --no-checkout --quiet
-			--repack-flags --user-log-author --localtime $remote_opts
+			--repack-flags --use-log-author --localtime
+			--ignore-paths= $remote_opts
 			"
 		local init_opts="
 			--template= --shared= --trunk= --tags=
 			--branches= --stdlayout --minimize-url
 			--no-metadata --use-svm-props --use-svnsync-props
-			--rewrite-root= $remote_opts
+			--rewrite-root= --prefix= --use-log-author
+			--add-author-from $remote_opts
 			"
 		local cmt_opts="
 			--edit --rmdir --find-copies-harder --copy-similarity=
 			"
 
-		local cur="${COMP_WORDS[COMP_CWORD]}"
 		case "$subcommand,$cur" in
 		fetch,--*)
 			__gitcomp "--revision= --fetch-all $fc_opts"
@@ -1632,27 +2526,28 @@
 		dcommit,--*)
 			__gitcomp "
 				--merge --strategy= --verbose --dry-run
-				--fetch-all --no-rebase $cmt_opts $fc_opts
+				--fetch-all --no-rebase --commit-url
+				--revision $cmt_opts $fc_opts
 				"
 			;;
 		set-tree,--*)
 			__gitcomp "--stdin $cmt_opts $fc_opts"
 			;;
 		create-ignore,--*|propget,--*|proplist,--*|show-ignore,--*|\
-		show-externals,--*)
+		show-externals,--*|mkdirs,--*)
 			__gitcomp "--revision="
 			;;
 		log,--*)
 			__gitcomp "
 				--limit= --revision= --verbose --incremental
 				--oneline --show-commit --non-recursive
-				--authors-file=
+				--authors-file= --color
 				"
 			;;
 		rebase,--*)
 			__gitcomp "
 				--merge --verbose --strategy= --local
-				--fetch-all $fc_opts
+				--fetch-all --dry-run $fc_opts
 				"
 			;;
 		commit-diff,--*)
@@ -1661,6 +2556,24 @@
 		info,--*)
 			__gitcomp "--url"
 			;;
+		branch,--*)
+			__gitcomp "--dry-run --message --tag"
+			;;
+		tag,--*)
+			__gitcomp "--dry-run --message"
+			;;
+		blame,--*)
+			__gitcomp "--git-format"
+			;;
+		migrate,--*)
+			__gitcomp "
+				--config-dir= --ignore-paths= --minimize
+				--no-auth-cache --username=
+				"
+			;;
+		reset,--*)
+			__gitcomp "--revision= --parent"
+			;;
 		*)
 			COMPREPLY=()
 			;;
@@ -1671,11 +2584,11 @@
 _git_tag ()
 {
 	local i c=1 f=0
-	while [ $c -lt $COMP_CWORD ]; do
-		i="${COMP_WORDS[c]}"
+	while [ $c -lt $cword ]; do
+		i="${words[c]}"
 		case "$i" in
 		-d|-v)
-			__gitcomp "$(__git_tags)"
+			__gitcomp_nl "$(__git_tags)"
 			return
 			;;
 		-f)
@@ -1685,29 +2598,49 @@
 		c=$((++c))
 	done
 
-	case "${COMP_WORDS[COMP_CWORD-1]}" in
+	case "$prev" in
 	-m|-F)
 		COMPREPLY=()
 		;;
 	-*|tag)
 		if [ $f = 1 ]; then
-			__gitcomp "$(__git_tags)"
+			__gitcomp_nl "$(__git_tags)"
 		else
 			COMPREPLY=()
 		fi
 		;;
 	*)
-		__gitcomp "$(__git_refs)"
+		__gitcomp_nl "$(__git_refs)"
 		;;
 	esac
 }
 
+_git_whatchanged ()
+{
+	_git_log
+}
+
 _git ()
 {
 	local i c=1 command __git_dir
 
-	while [ $c -lt $COMP_CWORD ]; do
-		i="${COMP_WORDS[c]}"
+	if [[ -n ${ZSH_VERSION-} ]]; then
+		emulate -L bash
+		setopt KSH_TYPESET
+
+		# workaround zsh's bug that leaves 'words' as a special
+		# variable in versions < 4.3.12
+		typeset -h words
+
+		# workaround zsh's bug that quotes spaces in the COMPREPLY
+		# array if IFS doesn't contain spaces.
+		typeset -h IFS
+	fi
+
+	local cur words cword prev
+	_get_comp_words_by_ref -n =: cur words cword prev
+	while [ $c -lt $cword ]; do
+		i="${words[c]}"
 		case "$i" in
 		--git-dir=*) __git_dir="${i#--git-dir=}" ;;
 		--bare)      __git_dir="." ;;
@@ -1719,7 +2652,7 @@
 	done
 
 	if [ -z "$command" ]; then
-		case "${COMP_WORDS[COMP_CWORD]}" in
+		case "$cur" in
 		--*)   __gitcomp "
 			--paginate
 			--no-pager
@@ -1727,84 +2660,60 @@
 			--bare
 			--version
 			--exec-path
+			--html-path
 			--work-tree=
+			--namespace=
 			--help
 			"
 			;;
-		*)     __gitcomp "$(__git_porcelain_commands) $(__git_aliases)" ;;
+		*)     __git_compute_porcelain_commands
+		       __gitcomp "$__git_porcelain_commands $(__git_aliases)" ;;
 		esac
 		return
 	fi
 
-	local expansion=$(__git_aliased_command "$command")
-	[ "$expansion" ] && command="$expansion"
+	local completion_func="_git_${command//-/_}"
+	declare -f $completion_func >/dev/null && $completion_func && return
 
-	case "$command" in
-	am)          _git_am ;;
-	add)         _git_add ;;
-	apply)       _git_apply ;;
-	archive)     _git_archive ;;
-	bisect)      _git_bisect ;;
-	bundle)      _git_bundle ;;
-	branch)      _git_branch ;;
-	checkout)    _git_checkout ;;
-	cherry)      _git_cherry ;;
-	cherry-pick) _git_cherry_pick ;;
-	clean)       _git_clean ;;
-	clone)       _git_clone ;;
-	commit)      _git_commit ;;
-	config)      _git_config ;;
-	describe)    _git_describe ;;
-	diff)        _git_diff ;;
-	fetch)       _git_fetch ;;
-	format-patch) _git_format_patch ;;
-	gc)          _git_gc ;;
-	grep)        _git_grep ;;
-	help)        _git_help ;;
-	init)        _git_init ;;
-	log)         _git_log ;;
-	ls-files)    _git_ls_files ;;
-	ls-remote)   _git_ls_remote ;;
-	ls-tree)     _git_ls_tree ;;
-	merge)       _git_merge;;
-	mergetool)   _git_mergetool;;
-	merge-base)  _git_merge_base ;;
-	mv)          _git_mv ;;
-	name-rev)    _git_name_rev ;;
-	pull)        _git_pull ;;
-	push)        _git_push ;;
-	rebase)      _git_rebase ;;
-	remote)      _git_remote ;;
-	reset)       _git_reset ;;
-	revert)      _git_revert ;;
-	rm)          _git_rm ;;
-	send-email)  _git_send_email ;;
-	shortlog)    _git_shortlog ;;
-	show)        _git_show ;;
-	show-branch) _git_show_branch ;;
-	stash)       _git_stash ;;
-	stage)       _git_add ;;
-	submodule)   _git_submodule ;;
-	svn)         _git_svn ;;
-	tag)         _git_tag ;;
-	whatchanged) _git_log ;;
-	*)           COMPREPLY=() ;;
-	esac
+	local expansion=$(__git_aliased_command "$command")
+	if [ -n "$expansion" ]; then
+		completion_func="_git_${expansion//-/_}"
+		declare -f $completion_func >/dev/null && $completion_func
+	fi
 }
 
 _gitk ()
 {
+	if [[ -n ${ZSH_VERSION-} ]]; then
+		emulate -L bash
+		setopt KSH_TYPESET
+
+		# workaround zsh's bug that leaves 'words' as a special
+		# variable in versions < 4.3.12
+		typeset -h words
+
+		# workaround zsh's bug that quotes spaces in the COMPREPLY
+		# array if IFS doesn't contain spaces.
+		typeset -h IFS
+	fi
+
+	local cur words cword prev
+	_get_comp_words_by_ref -n =: cur words cword prev
+
 	__git_has_doubledash && return
 
-	local cur="${COMP_WORDS[COMP_CWORD]}"
-	local g="$(git rev-parse --git-dir 2>/dev/null)"
+	local g="$(__gitdir)"
 	local merge=""
-	if [ -f $g/MERGE_HEAD ]; then
+	if [ -f "$g/MERGE_HEAD" ]; then
 		merge="--merge"
 	fi
 	case "$cur" in
 	--*)
-		__gitcomp "--not --all $merge"
+		__gitcomp "
+			$__git_log_common_options
+			$__git_log_gitk_options
+			$merge
+			"
 		return
 		;;
 	esac
@@ -1815,3 +2724,42 @@
 	|| complete -o default -o nospace -F _git git
 complete -o bashdefault -o default -o nospace -F _gitk gitk 2>/dev/null \
 	|| complete -o default -o nospace -F _gitk gitk
+
+# The following are necessary only for Cygwin, and only are needed
+# when the user has tab-completed the executable name and consequently
+# included the '.exe' suffix.
+#
+if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then
+complete -o bashdefault -o default -o nospace -F _git git.exe 2>/dev/null \
+	|| complete -o default -o nospace -F _git git.exe
+fi
+
+if [[ -n ${ZSH_VERSION-} ]]; then
+	__git_shopt () {
+		local option
+		if [ $# -ne 2 ]; then
+			echo "USAGE: $0 (-q|-s|-u) <option>" >&2
+			return 1
+		fi
+		case "$2" in
+		nullglob)
+			option="$2"
+			;;
+		*)
+			echo "$0: invalid option: $2" >&2
+			return 1
+		esac
+		case "$1" in
+		-q)	setopt | grep -q "$option" ;;
+		-u)	unsetopt "$option" ;;
+		-s)	setopt "$option" ;;
+		*)
+			echo "$0: invalid flag: $1" >&2
+			return 1
+		esac
+	}
+else
+	__git_shopt () {
+		shopt "$@"
+	}
+fi
diff --git a/completion/available/tmux.completion.bash b/completion/available/tmux.completion.bash
index c872ec5..a1b8f06 100644
--- a/completion/available/tmux.completion.bash
+++ b/completion/available/tmux.completion.bash
@@ -5,6 +5,90 @@
 # Usage: Put "source bash_completion_tmux.sh" into your .bashrc
 # Based upon the example at http://paste-it.appspot.com/Pj4mLycDE
 
+    _tmux_cmds=" \
+attach-session \
+bind-key \
+break-pane \
+capture-pane \
+choose-client \
+choose-session \
+choose-window \
+clear-history \
+clock-mode \
+command-prompt \
+confirm-before \
+copy-buffer \
+copy-mode \
+delete-buffer \
+detach-client \
+display-message \
+display-panes \
+down-pane \
+find-window \
+has-session \
+if-shell \
+join-pane \
+kill-pane \
+kill-server \
+kill-session \
+kill-window \
+last-window \
+link-window \
+list-buffers \
+list-clients \
+list-commands \
+list-keys \
+list-panes \
+list-sessions \
+list-windows \
+load-buffer \
+lock-client \
+lock-server \
+lock-session \
+move-window \
+new-session \
+new-window \
+next-layout \
+next-window \
+paste-buffer \
+pipe-pane \
+previous-layout \
+previous-window \
+refresh-client \
+rename-session \
+rename-window \
+resize-pane \
+respawn-window \
+rotate-window \
+run-shell \
+save-buffer \
+select-layout \
+select-pane \
+select-prompt \
+select-window \
+send-keys \
+send-prefix \
+server-info \
+set-buffer \
+set-environment \
+set-option \
+set-window-option \
+show-buffer \
+show-environment \
+show-messages \
+show-options \
+show-window-options \
+source-file \
+split-window \
+start-server \
+suspend-client \
+swap-pane \
+swap-window \
+switch-client \
+unbind-key \
+unlink-window \
+up-pane"
+
 _tmux_expand () 
 { 
     [ "$cur" != "${cur%\\}" ] && cur="$cur"'\';
@@ -34,12 +118,12 @@
 function _tmux_complete_client() {
     local IFS=$'\n'
     local cur="${1}"
-    COMPREPLY=( ${COMPREPLY[@]:-} $(compgen -W "$(tmux -q list-clients | cut -f 1 -d ':')" -- "${cur}") )
+    COMPREPLY=( ${COMPREPLY[@]:-} $(compgen -W "$(tmux -q list-clients 2>/dev/null | cut -f 1 -d ':')" -- "${cur}") )
 }
 function _tmux_complete_session() {
     local IFS=$'\n'
     local cur="${1}"
-    COMPREPLY=( ${COMPREPLY[@]:-} $(compgen -W "$(tmux -q list-sessions | cut -f 1 -d ':')" -- "${cur}") )
+    COMPREPLY=( ${COMPREPLY[@]:-} $(compgen -W "$(tmux -q list-sessions 2>/dev/null | cut -f 1 -d ':')" -- "${cur}") )
 }
 function _tmux_complete_window() {
     local IFS=$'\n'
@@ -47,10 +131,10 @@
     local session_name="$(echo "${cur}" | sed 's/\\//g' | cut -d ':' -f 1)"
     local sessions
     
-    sessions="$(tmux -q list-sessions | sed -re 's/([^:]+:).*$/\1/')"
+    sessions="$(tmux -q list-sessions 2>/dev/null | sed -re 's/([^:]+:).*$/\1/')"
     if [[ -n "${session_name}" ]]; then
         sessions="${sessions}
-$(tmux -q list-windows -t "${session_name}" | sed -re 's/^([^:]+):.*$/'"${session_name}"':\1/')"
+$(tmux -q list-windows -t "${session_name}" 2>/dev/null | sed -re 's/^([^:]+):.*$/'"${session_name}"':\1/')"
     fi
     cur="$(echo "${cur}" | sed -e 's/:/\\\\:/')"
     sessions="$(echo "${sessions}" | sed -e 's/:/\\\\:/')"
@@ -102,8 +186,7 @@
 
     if [[ $COMP_CWORD -le $cmd_index ]]; then
         # The user has not specified a command yet
-        local all_commands="$(tmux -q list-commands | cut -f 1 -d ' ')"
-        COMPREPLY=( ${COMPREPLY[@]:-} $(compgen -W "${all_commands}" -- "${cur}") )
+        COMPREPLY=( ${COMPREPLY[@]:-} $(compgen -W "${_tmux_cmds}" -- "${cur}") )
     else        
         case ${cmd} in
             attach-session|attach)
diff --git a/install.sh b/install.sh
index a80a749..0f1da39 100755
--- a/install.sh
+++ b/install.sh
@@ -31,23 +31,33 @@
 function load_all() {
   file_type=$1
   [ ! -d "$BASH_IT/$file_type/enabled" ] && mkdir "$BASH_IT/${file_type}/enabled"
-  ln -s $BASH_IT/${file_type}/available/* "${BASH_IT}/${file_type}/enabled"
+  for src in $BASH_IT/${file_type}/available/*; do
+      filename="$(basename ${src})"
+      [ ${filename:0:1} = "_" ] && continue
+      dest="${BASH_IT}/${file_type}/enabled/${filename}"
+      if [ ! -e "${dest}" ]; then
+          ln -s "${src}" "${dest}"
+      else
+          echo "File ${dest} exists, skipping"
+      fi
+  done
 }
 
 function load_some() {
     file_type=$1
-    for file in `ls $BASH_IT/${file_type}/available`
+    for path in `ls $BASH_IT/${file_type}/available/[^_]*`
     do
       if [ ! -d "$BASH_IT/$file_type/enabled" ]
       then
         mkdir "$BASH_IT/$file_type/enabled"
       fi
+      file_name=$(basename "$path")
       while true
       do
-        read -p "Would you like to enable the ${file%.*.*} $file_type? [Y/N] " RESP
+        read -p "Would you like to enable the ${file_name%%.*} $file_type? [Y/N] " RESP
         case $RESP in
         [yY])
-          ln -s "$BASH_IT/$file_type/available/$file" "$BASH_IT/$file_type/enabled"
+          ln -s "$path" "$BASH_IT/$file_type/enabled"
           break
           ;;
         [nN])
diff --git a/lib/composure.sh b/lib/composure.sh
new file mode 100644
index 0000000..beaf13a
--- /dev/null
+++ b/lib/composure.sh
@@ -0,0 +1,438 @@
+# composure - by erichs
+# light-hearted functions for intuitive shell programming
+
+# install: source this script in your ~/.profile or ~/.${SHELL}rc script
+
+# latest source available at http://git.io/composure
+# known to work on bash, zsh, and ksh93
+
+# 'plumbing' functions
+
+composure_keywords ()
+{
+    echo "about author example group param version"
+}
+
+letterpress ()
+{
+    typeset rightcol="$1" leftcol="${2:- }"
+
+    if [ -z "$rightcol" ]; then
+        return
+    fi
+
+    printf "%-20s%s\n" "$leftcol" "$rightcol"
+}
+
+transcribe ()
+{
+    typeset func=$1
+    typeset file=$2
+    typeset operation="$3"
+
+    if git --version >/dev/null 2>&1; then
+        if [ -d ~/.composure ]; then
+            (
+                cd ~/.composure
+                if git rev-parse 2>/dev/null; then
+                    if [ ! -f $file ]; then
+                        printf "%s\n" "Oops! Couldn't find $file to version it for you..."
+                        return
+                    fi
+                    cp $file ~/.composure/$func.inc
+                    git add --all .
+                    git commit -m "$operation $func"
+                fi
+            )
+        else
+            if [ "$USE_COMPOSURE_REPO" = "0" ]; then
+                return  # if you say so...
+            fi
+            printf "%s\n" "I see you don't have a ~/.composure repo..."
+            typeset input
+            typeset valid=0
+            while [ $valid != 1 ]; do
+                printf "\n%s" 'would you like to create one? y/n: '
+                read input
+                case $input in
+                    y|yes|Y|Yes|YES)
+                        (
+                            echo 'creating git repository for your functions...'
+                            mkdir ~/.composure
+                            cd ~/.composure
+                            git init
+                            echo "composure stores your function definitions here" > README.txt
+                            git add README.txt
+                            git commit -m 'initial commit'
+                        )
+                        # if at first you don't succeed...
+                        transcribe "$func" "$file" "$operation"
+                        valid=1
+                        ;;
+                    n|no|N|No|NO)
+                        printf "%s\n" "ok. add 'export USE_COMPOSURE_REPO=0' to your startup script to disable this message."
+                        valid=1
+                    ;;
+                    *)
+                        printf "%s\n" "sorry, didn't get that..."
+                    ;;
+                esac
+            done
+       fi
+    fi
+}
+
+transcribe ()
+{
+    typeset func=$1
+    typeset file=$2
+    typeset operation="$3"
+
+    if git --version >/dev/null 2>&1; then
+        if [ -d ~/.composure ]; then
+            (
+                cd ~/.composure
+                if git rev-parse 2>/dev/null; then
+                    if [ ! -f $file ]; then
+                        printf "%s\n" "Oops! Couldn't find $file to version it for you..."
+                        return
+                    fi
+                    cp $file ~/.composure/$func.inc
+                    git add --all .
+                    git commit -m "$operation $func"
+                fi
+            )
+        else
+            if [ "$USE_COMPOSURE_REPO" = "0" ]; then
+                return  # if you say so...
+            fi
+            printf "%s\n" "I see you don't have a ~/.composure repo..."
+            typeset input
+            typeset valid=0
+            while [ $valid != 1 ]; do
+                printf "\n%s" 'would you like to create one? y/n: '
+                read input
+                case $input in
+                    y|yes|Y|Yes|YES)
+                        (
+                            echo 'creating git repository for your functions...'
+                            mkdir ~/.composure
+                            cd ~/.composure
+                            git init
+                            echo "composure stores your function definitions here" > README.txt
+                            git add README.txt
+                            git commit -m 'initial commit'
+                        )
+                        # if at first you don't succeed...
+                        transcribe "$func" "$file" "$operation"
+                        valid=1
+                        ;;
+                    n|no|N|No|NO)
+                        printf "%s\n" "ok. add 'export USE_COMPOSURE_REPO=0' to your startup script to disable this message."
+                        valid=1
+                    ;;
+                    *)
+                        printf "%s\n" "sorry, didn't get that..."
+                    ;;
+                esac
+            done
+       fi
+    fi
+}
+
+typeset_functions ()
+{
+    # unfortunately, there does not seem to be a easy, portable way to list just the
+    # names of the defined shell functions...
+
+    # first, determine our shell:
+    typeset shell
+    if [ -n "$SHELL" ]; then
+        shell=$(basename $SHELL)  # we assume this is set correctly!
+    else
+        # we'll have to try harder
+        # here's a hack I modified from a StackOverflow post:
+        # we loop over the ps listing for the current process ($$), and print the last column (CMD)
+        # stripping any leading hyphens bash sometimes throws in there
+        typeset x ans
+        typeset this=$(for x in $(ps -p $$); do ans=$x; done; printf "%s\n" $ans | sed 's/^-*//')
+        typeset shell=$(basename $this)  # e.g. /bin/bash => bash
+    fi
+    case "$shell" in
+        bash)
+            typeset -F | awk '{print $3}'
+            ;;
+        *)
+            # trim everything following '()' in ksh
+            typeset +f | sed 's/().*$//'
+            ;;
+    esac
+}
+
+
+# bootstrap metadata keywords for porcelain functions
+for f in $(composure_keywords)
+do
+    eval "$f() { :; }"
+done
+unset f
+
+
+# 'porcelain' functions
+
+cite ()
+{
+    about creates one or more meta keywords for use in your functions
+    param one or more keywords
+    example '$ cite url username'
+    example '$ url http://somewhere.com'
+    example '$ username alice'
+    group composure
+
+    # this is the storage half of the 'metadata' system:
+    # we create dynamic metadata keywords with function wrappers around
+    # the NOP command, ':'
+
+    # anything following a keyword will get parsed as a positional
+    # parameter, but stay resident in the ENV. As opposed to shell
+    # comments, '#', which do not get parsed and are not available
+    # at runtime.
+
+    # a BIG caveat--your metadata must be roughly parsable: do not use
+    # contractions, and consider single or double quoting if it contains
+    # non-alphanumeric characters
+
+    if [ -z "$1" ]; then
+        printf '%s\n' 'missing parameter(s)'
+        reference cite
+        return
+    fi
+
+    typeset keyword
+    for keyword in $*; do
+        eval "$keyword() { :; }"
+    done
+}
+
+draft ()
+{
+    about wraps command from history into a new function, default is last command
+    param 1: name to give function
+    param 2: optional history line number
+    example '$ ls'
+    example '$ draft list'
+    example '$ draft newfunc 1120  # wraps command at history line 1120 in newfunc()'
+    group composure
+
+    typeset func=$1
+    typeset num=$2
+    typeset cmd
+
+    if [ -z "$func" ]; then
+        printf '%s\n' 'missing parameter(s)'
+        reference draft
+        return
+    fi
+
+    # aliases bind tighter than function names, disallow them
+    if [ -n "$(type -a $func 2>/dev/null | grep 'is.*alias')" ]; then
+        printf '%s\n' "sorry, $(type -a $func). please choose another name."
+        return
+    fi
+
+    if [ -z "$num" ]; then
+        # parse last command from fc output
+        # some versions of 'fix command, fc' need corrective lenses...
+        typeset myopic=$(fc -ln -1 | grep draft)
+        typeset lines=1
+        if [ -n "$myopic" ]; then
+            lines=2
+        fi
+        cmd=$(fc -ln -$lines | head -1 | sed 's/^[[:blank:]]*//')
+    else
+        # parse command from history line number
+        cmd=$(eval "history | grep '^[[:blank:]]*$num' | head -1" | sed 's/^[[:blank:][:digit:]]*//')
+    fi
+    eval "$func() { $cmd; }"
+    typeset file=$(mktemp /tmp/draft.XXXX)
+    typeset -f $func > $file
+    transcribe $func $file draft
+    rm $file 2>/dev/null
+}
+
+glossary ()
+{
+    about displays help summary for all functions, or summary for a group of functions
+    param 1: optional, group name
+    example '$ glossary'
+    example '$ glossary misc'
+    group composure
+
+    typeset targetgroup=${1:-}
+
+    for func in $(typeset_functions); do
+        if [ -n "$targetgroup" ]; then
+            typeset group="$(typeset -f $func | metafor group)"
+            if [ "$group" != "$targetgroup" ]; then
+                continue  # skip non-matching groups, if specified
+            fi
+        fi
+        typeset about="$(typeset -f $func | metafor about)"
+        letterpress "$about" $func
+    done
+}
+
+metafor ()
+{
+    about prints function metadata associated with keyword
+    param 1: meta keyword
+    example '$ typeset -f glossary | metafor example'
+    group composure
+
+    typeset keyword=$1
+
+    if [ -z "$keyword" ]; then
+        printf '%s\n' 'missing parameter(s)'
+        reference metafor
+        return
+    fi
+
+    # this sed-fu is the retrieval half of the 'metadata' system:
+    # 'grep' for the metadata keyword, and then parse/filter the matching line
+
+    # grep keyword # strip trailing '|"|; # ignore thru keyword and leading '|"
+    sed -n "/$keyword / s/['\";]*$//;s/^[ 	]*$keyword ['\"]*\([^([].*\)*$/\1/p"
+}
+
+reference ()
+{
+    about displays apidoc help for a specific function
+    param 1: function name
+    example '$ reference revise'
+    group composure
+
+    typeset func=$1
+    if [ -z "$func" ]; then
+        printf '%s\n' 'missing parameter(s)'
+        reference reference
+        return
+    fi
+
+    typeset line
+
+    typeset about="$(typeset -f $func | metafor about)"
+    letterpress "$about" $func
+
+    typeset author="$(typeset -f $func | metafor author)"
+    if [ -n "$author" ]; then
+        letterpress "$author" 'author:'
+    fi
+
+    typeset version="$(typeset -f $func | metafor version)"
+    if [ -n "$version" ]; then
+        letterpress "$version" 'version:'
+    fi
+
+    if [ -n "$(typeset -f $func | metafor param)" ]; then
+        printf "parameters:\n"
+        typeset -f $func | metafor param | while read line
+        do
+            letterpress "$line"
+        done
+    fi
+
+    if [ -n "$(typeset -f $func | metafor example)" ]; then
+        printf "examples:\n"
+        typeset -f $func | metafor example | while read line
+        do
+            letterpress "$line"
+        done
+    fi
+}
+
+revise ()
+{
+    about loads function into editor for revision
+    param 1: name of function
+    example '$ revise myfunction'
+    group composure
+
+    typeset func=$1
+    typeset temp=$(mktemp /tmp/revise.XXXX)
+
+    if [ -z "$func" ]; then
+        printf '%s\n' 'missing parameter(s)'
+        reference revise
+        return
+    fi
+
+    # populate tempfile...
+    if [ -f ~/.composure/$func.inc ]; then
+        # ...with contents of latest git revision...
+        cat ~/.composure/$func.inc >> $temp
+    else
+        # ...or from ENV if not previously versioned
+        typeset -f $func >> $temp
+    fi
+
+    if [ -z "$EDITOR" ]
+    then
+      typeset EDITOR=vi
+    fi
+
+    $EDITOR $temp
+    . $temp  # source edited file
+
+    transcribe $func $temp revise
+    rm $temp
+}
+
+write ()
+{
+    about writes one or more composed function definitions to stdout
+    param one or more function names
+    example '$ write finddown foo'
+    example '$ write finddown'
+    group composure
+
+    if [ -z "$1" ]; then
+        printf '%s\n' 'missing parameter(s)'
+        reference write
+        return
+    fi
+
+# bootstrap metadata
+cat <<END
+for f in $(composure_keywords)
+do
+    eval "\$f() { :; }"
+done
+unset f
+END
+
+    # include cite() to enable custom keywords
+    typeset -f cite $*
+}
+
+: <<EOF
+License: The MIT License
+
+Copyright © 2012 Erich Smith
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this
+software and associated documentation files (the "Software"), to deal in the Software
+without restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be included in all copies
+or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+EOF
diff --git a/lib/helpers.bash b/lib/helpers.bash
index ec6587e..e1787fe 100644
--- a/lib/helpers.bash
+++ b/lib/helpers.bash
@@ -1,11 +1,11 @@
 # Helper function loading various enable-able files
 function _load_bash_it_files() {
-  file_type="$1"
-  if [ ! -d "${BASH_IT}/${file_type}/enabled" ]
+  subdirectory="$1"
+  if [ ! -d "${BASH_IT}/${subdirectory}/enabled" ]
   then
     continue
   fi
-  FILES="${BASH_IT}/${file_type}/enabled/*.bash"
+  FILES="${BASH_IT}/${subdirectory}/enabled/*.bash"
   for config_file in $FILES
   do
     if [ -e "${config_file}" ]; then
@@ -28,3 +28,326 @@
 function reload_plugins() {
   _load_bash_it_files "plugins"
 }
+
+bash-it ()
+{
+    about 'bash-it help and maintenance'
+    param '1: verb [one of: help | show | enable | disable ]'
+    param '2: component type [one of: alias(es) | completion(s) | plugin(s) ]'
+    param '3: specific component [optional]'
+    example '$ bash-it show plugins'
+    example '$ bash-it help aliases'
+    example '$ bash-it enable plugin git'
+    example '$ bash-it disable alias hg'
+    typeset verb=${1:-}
+    shift
+    typeset component=${1:-}
+    shift
+    typeset func
+    case $verb in
+         show)
+             func=_bash-it-$component;;
+         enable)
+             func=_enable-$component;;
+         disable)
+             func=_disable-$component;;
+         help)
+             func=_help-$component;;
+         *)
+             reference bash-it
+             return;;
+    esac
+
+    # pluralize component if necessary
+    if ! _is_function $func; then
+        if _is_function ${func}s; then
+            func=${func}s
+        else
+            if _is_function ${func}es; then
+                func=${func}es
+            else
+                echo "oops! $component is not a valid option!"
+                reference bash-it
+                return
+            fi
+        fi
+    fi
+    $func $*
+}
+
+_is_function ()
+{
+    _about 'sets $? to true if parameter is the name of a function'
+    _param '1: name of alleged function'
+    _group 'lib'
+    [ -n "$(type -a $1 2>/dev/null | grep 'is a function')" ]
+}
+
+_bash-it-aliases ()
+{
+    _about 'summarizes available bash_it aliases'
+    _group 'lib'
+
+    _bash-it-describe "aliases" "an" "alias" "Alias"
+}
+
+_bash-it-completions ()
+{
+    _about 'summarizes available bash_it completions'
+    _group 'lib'
+
+    _bash-it-describe "completion" "a" "completion" "Completion"
+}
+
+_bash-it-plugins ()
+{
+    _about 'summarizes available bash_it plugins'
+    _group 'lib'
+
+    _bash-it-describe "plugins" "a" "plugin" "Plugin"
+}
+
+_bash-it-describe ()
+{
+    _about 'summarizes available bash_it components'
+    _param '1: subdirectory'
+    _param '2: preposition'
+    _param '3: file_type'
+    _param '4: column_header'
+    _example '$ _bash-it-describe "plugins" "a" "plugin" "Plugin"'
+    
+    subdirectory="$1"
+    preposition="$2"
+    file_type="$3"
+    column_header="$4"
+
+    typeset f
+    typeset enabled
+    printf "%-20s%-10s%s\n" "$column_header" 'Enabled?' 'Description'
+    for f in $BASH_IT/$subdirectory/available/*.bash
+    do
+        if [ -e $BASH_IT/$subdirectory/enabled/$(basename $f) ]; then
+            enabled='x'
+        else
+            enabled=' '
+        fi
+        printf "%-20s%-10s%s\n" "$(basename $f | cut -d'.' -f1)" "  [$enabled]" "$(cat $f | metafor about-$file_type)"
+    done
+    printf '\n%s\n' "to enable $preposition $file_type, do:"
+    printf '%s\n' "$ bash-it enable $file_type  <$file_type name> -or- $ bash-it enable $file_type all"
+    printf '\n%s\n' "to disable $preposition $file_type, do:"
+    printf '%s\n' "$ bash-it disable $file_type <$file_type name> -or- $ bash-it disable $file_type all"
+}
+
+_disable-plugin ()
+{
+    _about 'disables bash_it plugin'
+    _param '1: plugin name'
+    _example '$ disable-plugin rvm'
+    _group 'lib'
+
+    _disable-thing "plugins" "plugin" $1
+}
+
+_disable-alias ()
+{
+    _about 'disables bash_it alias'
+    _param '1: alias name'
+    _example '$ disable-alias git'
+    _group 'lib'
+
+    _disable-thing "aliases" "alias" $1
+}
+
+_disable-completion ()
+{
+    _about 'disables bash_it completion'
+    _param '1: completion name'
+    _example '$ disable-completion git'
+    _group 'lib'
+
+    _disable-thing "completion" "completion" $1
+}
+
+_disable-thing ()
+{
+    _about 'disables a bash_it component'
+    _param '1: subdirectory'
+    _param '2: file_type'
+    _param '3: file_entity'
+    _example '$ _disable-thing "plugins" "plugin" "ssh"'
+    
+    subdirectory="$1"
+    file_type="$2"
+    file_entity="$3"
+
+    if [ -z "$file_entity" ]; then
+        reference "disable-$file_type"
+        return
+    fi
+
+    if [ "$file_entity" = "all" ]; then
+        typeset f $file_type
+        for f in $BASH_IT/$subdirectory/available/*.bash
+        do
+            plugin=$(basename $f)
+            if [ -e $BASH_IT/$subdirectory/enabled/$plugin ]; then
+                rm $BASH_IT/$subdirectory/enabled/$(basename $plugin)
+            fi
+        done
+    else
+        typeset plugin=$(command ls $BASH_IT/$subdirectory/enabled/$file_entity.*bash 2>/dev/null | head -1)
+        if [ -z "$plugin" ]; then
+            printf '%s\n' "sorry, that does not appear to be an enabled $file_type."
+            return
+        fi
+        rm $BASH_IT/$subdirectory/enabled/$(basename $plugin)
+    fi
+
+    printf '%s\n' "$file_entity disabled."
+}
+
+_enable-plugin ()
+{
+    _about 'enables bash_it plugin'
+    _param '1: plugin name'
+    _example '$ enable-plugin rvm'
+    _group 'lib'
+
+    _enable-thing "plugins" "plugin" $1
+}
+
+_enable-alias ()
+{
+    _about 'enables bash_it alias'
+    _param '1: alias name'
+    _example '$ enable-alias git'
+    _group 'lib'
+
+    _enable-thing "aliases" "alias" $1
+}
+
+_enable-completion ()
+{
+    _about 'enables bash_it completion'
+    _param '1: completion name'
+    _example '$ enable-completion git'
+    _group 'lib'
+
+    _enable-thing "completion" "completion" $1
+}
+
+_enable-thing ()
+{
+    cite _about _param _example
+    _about 'enables a bash_it component'
+    _param '1: subdirectory'
+    _param '2: file_type'
+    _param '3: file_entity'
+    _example '$ _enable-thing "plugins" "plugin" "ssh"'	
+	
+    subdirectory="$1"
+    file_type="$2"
+    file_entity="$3"
+
+    if [ -z "$file_entity" ]; then
+        reference "enable-$file_type"
+        return
+    fi
+
+    if [ "$file_entity" = "all" ]; then
+        typeset f $file_type
+        for f in $BASH_IT/$subdirectory/available/*.bash
+        do
+            plugin=$(basename $f)
+            if [ ! -h $BASH_IT/$subdirectory/enabled/$plugin ]; then
+                ln -s $BASH_IT/$subdirectory/available/$plugin $BASH_IT/$subdirectory/enabled/$plugin
+            fi
+        done
+    else
+        typeset plugin=$(command ls $BASH_IT/$subdirectory/available/$file_entity.*bash 2>/dev/null | head -1)
+        if [ -z "$plugin" ]; then
+            printf '%s\n' "sorry, that does not appear to be an available $file_type."
+            return
+        fi
+
+        plugin=$(basename $plugin)
+        if [ -e $BASH_IT/$subdirectory/enabled/$plugin ]; then
+            printf '%s\n' "$file_entity is already enabled."
+            return
+        fi
+
+        ln -s $BASH_IT/$subdirectory/available/$plugin $BASH_IT/$subdirectory/enabled/$plugin
+    fi
+
+    printf '%s\n' "$file_entity enabled."
+}
+
+_help-aliases()
+{
+    _about 'shows help for all aliases, or a specific alias group'
+    _param '1: optional alias group'
+    _example '$ alias-help'
+    _example '$ alias-help git'
+
+    if [ -n "$1" ]; then
+        cat $BASH_IT/aliases/available/$1.aliases.bash | metafor alias | sed "s/$/'/"
+    else
+        typeset f
+        for f in $BASH_IT/aliases/enabled/*
+        do
+            typeset file=$(basename $f)
+            printf '\n\n%s:\n' "${file%%.*}"
+            # metafor() strips trailing quotes, restore them with sed..
+            cat $f | metafor alias | sed "s/$/'/"
+        done
+    fi
+}
+
+_help-plugins()
+{
+    _about 'summarize all functions defined by enabled bash-it plugins'
+    _group 'lib'
+
+    # display a brief progress message...
+    printf '%s' 'please wait, building help...'
+    typeset grouplist=$(mktemp /tmp/grouplist.XXXX)
+    typeset func
+    for func in $(typeset_functions)
+    do
+        typeset group="$(typeset -f $func | metafor group)"
+        if [ -z "$group" ]; then
+            group='misc'
+        fi
+        typeset about="$(typeset -f $func | metafor about)"
+        letterpress "$about" $func >> $grouplist.$group
+        echo $grouplist.$group >> $grouplist
+    done
+    # clear progress message
+    printf '\r%s\n' '                              '
+    typeset group
+    typeset gfile
+    for gfile in $(cat $grouplist | sort | uniq)
+    do
+        printf '%s\n' "${gfile##*.}:"
+        cat $gfile
+        printf '\n'
+        rm $gfile 2> /dev/null
+    done | less
+    rm $grouplist 2> /dev/null
+}
+
+all_groups ()
+{
+    about 'displays all unique metadata groups'
+    group 'lib'
+
+    typeset func
+    typeset file=$(mktemp /tmp/composure.XXXX)
+    for func in $(typeset_functions)
+    do
+        typeset -f $func | metafor group >> $file
+    done
+    cat $file | sort | uniq
+    rm $file
+}
diff --git a/plugins/available/_xterm.plugin.bash b/plugins/available/_xterm.plugin.bash
new file mode 100644
index 0000000..b3810e7
--- /dev/null
+++ b/plugins/available/_xterm.plugin.bash
@@ -0,0 +1,28 @@
+# This plugin is known to cause issues on OS X with the evaluation of
+# colors.  Please read [issue 108] for more information.
+#
+# You can manually turn this on by symlinking it into your
+# plugins/enabled/ directory.
+#
+# [issue 108]: https://github.com/revans/bash-it/issues/108
+
+cite about-plugin
+about-plugin 'automatically set your xterm title with host and location info'
+
+set_xterm_title () {
+    local title="$1"
+    echo -ne "\e]0;$title\007"
+}
+
+
+precmd () {
+    set_xterm_title "${USER}@${HOSTNAME} `dirs -0` $PROMPTCHAR"
+}
+
+preexec () {
+    set_xterm_title "$1 {`dirs -0`} (${USER}@${HOSTNAME})"
+}
+
+case "$TERM" in
+    xterm*|rxvt*) preexec_install;;
+esac
diff --git a/plugins/available/base.plugin.bash b/plugins/available/base.plugin.bash
index 3edd8ad..e07401b 100644
--- a/plugins/available/base.plugin.bash
+++ b/plugins/available/base.plugin.bash
@@ -1,85 +1,140 @@
-#!/usr/bin/env bash
+cite about-plugin
+about-plugin 'miscellaneous tools'
 
-# For generic functions.
-
-function ips {
-  ifconfig | grep "inet " | awk '{ print $2 }'
+ips ()
+{
+    about 'display all ip addresses for this host'
+    group 'base'
+    ifconfig | grep "inet " | awk '{ print $2 }'
 }
 
-function down4me() {
-  curl -s "http://www.downforeveryoneorjustme.com/$1" | sed '/just you/!d;s/<[^>]*>//g'
+down4me ()
+{
+    about 'checks whether a website is down for you, or everybody'
+    param '1: website url'
+    example '$ down4me http://www.google.com'
+    group 'base'
+    curl -s "http://www.downforeveryoneorjustme.com/$1" | sed '/just you/!d;s/<[^>]*>//g'
 }
 
-function myip {
-  res=$(curl -s checkip.dyndns.org | grep -Eo '[0-9\.]+')
-  echo -e "Your public IP is: ${echo_bold_green} $res ${echo_normal}"
-}
-
-pass() {
-  which gshuf &> /dev/null
-  if [ $? -eq 1 ]
-  then
-    echo "Error: shuf isn't installed!"
-    return 1
-  fi
-
-  pass=$(shuf -n4 /usr/share/dict/words | tr '\n' ' ')
-  echo "With spaces (easier to memorize): $pass"
-  echo "Without (use this as the pass): $(echo $pass | tr -d ' ')"
-}
-
-# Function for previewing markdown files in the browser
-
-function pmdown() {
-  if command -v markdown &>/dev/null
-  then
-    markdown $1 | browser
-  else
-    echo "You don't have a markdown command installed!"
-  fi
-}
-
-# Make a directory and immediately 'cd' into it
-
-function mkcd() {
-  mkdir -p "$*"
-  cd "$*"
-}
-
-# Search through directory contents with grep
-
-function lsgrep(){
-  ls | grep "$*"
-}
-
-# View man documentation in Preview
-pman () {
-   man -t "${1}" | open -f -a $PREVIEW
+myip ()
+{
+    about 'displays your ip address, as seen by the Internet'
+    group 'base'
+    res=$(curl -s checkip.dyndns.org | grep -Eo '[0-9\.]+')
+    echo -e "Your public IP is: ${echo_bold_green} $res ${echo_normal}"
 }
 
 
-pcurl() {
-  curl "${1}" | open -f -a $PREVIEW
+pickfrom ()
+{
+    about 'picks random line from file'
+    param '1: filename'
+    example '$ pickfrom /usr/share/dict/words'
+    group 'base'
+    local file=$1
+    [ -z "$file" ] && reference $FUNCNAME && return
+    length=$(cat $file | wc -l)
+    n=$(expr $RANDOM \* $length \/ 32768 + 1)
+    head -n $n $file | tail -1
 }
 
-pri() {
-  ri -T "${1}" | open -f -a $PREVIEW
+pass ()
+{
+    about 'generates random password from dictionary words'
+    param 'optional integer length'
+    param 'if unset, defaults to 4'
+    example '$ pass'
+    example '$ pass 6'
+    group 'base'
+    local i pass length=${1:-4}
+    pass=$(echo $(for i in $(eval echo "{1..$length}"); do pickfrom /usr/share/dict/words; done))
+    echo "With spaces (easier to memorize): $pass"
+    echo "Without (use this as the pass): $(echo $pass | tr -d ' ')"
 }
 
-quiet() {
+pmdown ()
+{
+    about 'preview markdown file in a browser'
+    param '1: markdown file'
+    example '$ pmdown README.md'
+    group 'base'
+    if command -v markdown &>/dev/null
+    then
+      markdown $1 | browser
+    else
+      echo "You don't have a markdown command installed!"
+    fi
+}
+
+mkcd ()
+{
+    about 'make a directory and cd into it'
+    param 'path to create'
+    example '$ mkcd foo'
+    example '$ mkcd /tmp/img/photos/large'
+    group 'base'
+    mkdir -p "$*"
+    cd "$*"
+}
+
+lsgrep ()
+{
+    about 'search through directory contents with grep'
+    group 'base'
+    ls | grep "$*"
+}
+
+
+pman ()
+{
+    about 'view man documentation in Preview'
+    param '1: man page to view'
+    example '$ pman bash'
+    group 'base'
+    man -t "${1}" | open -f -a $PREVIEW
+}
+
+
+pcurl ()
+{
+    about 'download file and Preview it'
+    param '1: download URL'
+    example '$ pcurl http://www.irs.gov/pub/irs-pdf/fw4.pdf'
+    group 'base'
+    curl "${1}" | open -f -a $PREVIEW
+}
+
+pri ()
+{
+    about 'display information about Ruby classes, modules, or methods, in Preview'
+    param '1: Ruby method, module, or class'
+    example '$ pri Array'
+    group 'base'
+    ri -T "${1}" | open -f -a $PREVIEW
+}
+
+quiet ()
+{
+    about 'what *does* this do?'
+    group 'base'
 	$* &> /dev/null &
 }
 
-banish-cookies() {
+banish-cookies ()
+{
+    about 'redirect .adobe and .macromedia files to /dev/null'
+    group 'base'
 	rm -r ~/.macromedia ~/.adobe
 	ln -s /dev/null ~/.adobe
 	ln -s /dev/null ~/.macromedia
 }
 
-# disk usage per directory
-# in Mac OS X and Linux
 usage ()
 {
+    about 'disk usage per directory, in Mac OS X and Linux'
+    param '1: directory name'
+    group 'base'
     if [ $(uname) = "Darwin" ]; then
         if [ -n $1 ]; then
             du -hd $1
@@ -96,42 +151,37 @@
     fi
 }
 
-# One thing todo
-function t() {
-	 if [[ "$*" == "" ]] ; then
-		 cat ~/.t
-	 else
-		 echo "$*" > ~/.t
-	 fi
-}
+if [ ! -e $BASH_IT/plugins/enabled/todo.plugin.bash ]; then
+# if user has installed todo plugin, skip this...
+    t ()
+    {
+        about 'one thing todo'
+        param 'if not set, display todo item'
+        param '1: todo text'
+        if [[ "$*" == "" ]] ; then
+            cat ~/.t
+        else
+            echo "$*" > ~/.t
+        fi
+    }
+fi
 
-# Checks for existence of a command
-command_exists () {
+command_exists ()
+{
+    about 'checks for existence of a command'
+    param '1: command to check'
+    example '$ command_exists ls && echo exists'
+    group 'base'
     type "$1" &> /dev/null ;
 }
 
-# List all plugins and functions defined by bash-it
-function plugins-help() {
-    
-    echo "bash-it Plugins Help-Message"
-    echo 
-
-    set | grep "()" \
-    | sed -e "/^_/d" | grep -v "BASH_ARGC=()" \
-    | sed -e "/^\s/d" | grep -v "BASH_LINENO=()" \
-    | grep -v "BASH_ARGV=()" \
-    | grep -v "BASH_SOURCE=()" \
-    | grep -v "DIRSTACK=()" \
-    | grep -v "GROUPS=()" \
-    | grep -v "BASH_CMDS=()" \
-    | grep -v "BASH_ALIASES=()" \
-    | grep -v "COMPREPLY=()" | sed -e "s/()//"
-}
-
-# back up file with timestamp
 # useful for administrators and configs
-buf () {
-    filename=$1
-    filetime=$(date +%Y%m%d_%H%M%S)
+buf ()
+{
+    about 'back up file with timestamp'
+    param 'filename'
+    group 'base'
+    local filename=$1
+    local filetime=$(date +%Y%m%d_%H%M%S)
     cp ${filename} ${filename}_${filetime}
 }
diff --git a/plugins/available/battery.plugin.bash b/plugins/available/battery.plugin.bash
index 4b9d4b9..002d42f 100644
--- a/plugins/available/battery.plugin.bash
+++ b/plugins/available/battery.plugin.bash
@@ -1,11 +1,15 @@
-#!/usr/bin/env bash
+cite about-plugin
+about-plugin 'display info about your battery charge level'
 
 battery_percentage(){
+  about 'displays battery charge as a percentage of full (100%)'
+  group 'battery'
+
   if command_exists acpi;
   then
     local ACPI_OUTPUT=$(acpi -b)
     case $ACPI_OUTPUT in
-      *" Unknown"*) 
+      *" Unknown"*)
         local PERC_OUTPUT=$(echo $ACPI_OUTPUT | head -c 22 | tail -c 2)
         case $PERC_OUTPUT in
           *%)
@@ -16,7 +20,7 @@
             ;;
         esac
         ;;
-      *" Discharging"*) 
+      *" Discharging"*)
         local PERC_OUTPUT=$(echo $ACPI_OUTPUT | head -c 26 | tail -c 2)
         case $PERC_OUTPUT in
           *%)
@@ -27,7 +31,7 @@
             ;;
         esac
         ;;
-      *" Charging"*) 
+      *" Charging"*)
         local PERC_OUTPUT=$(echo $ACPI_OUTPUT | head -c 23 | tail -c 2)
         case $PERC_OUTPUT in
           *%)
@@ -38,7 +42,7 @@
             ;;
         esac
         ;;
-      *" Full"*) 
+      *" Full"*)
         echo '99'
         ;;
       *)
@@ -52,7 +56,7 @@
     #local IOREG_OUTPUT_10_5=$(ioreg -l | grep -i capacity | grep -v Legacy| tr '\n' ' | ' | awk '{printf("%.2f%%", $14/$7 * 100)}')
     local IOREG_OUTPUT=$(ioreg -n AppleSmartBattery -r | awk '$1~/Capacity/{c[$1]=$3} END{OFMT="%.2f%%"; max=c["\"MaxCapacity\""]; print (max>0? 100*c["\"CurrentCapacity\""]/max: "?")}')
     case $IOREG_OUTPUT in
-      100*) 
+      100*)
         echo '99'
         ;;
       *)
@@ -65,6 +69,9 @@
 }
 
 battery_charge(){
+  about 'graphical display of your battery charge'
+  group 'battery'
+
   # Full char
   local F_C='▸'
   # Depleted char
diff --git a/plugins/available/browser.plugin.bash b/plugins/available/browser.plugin.bash
index fdfba4f..f7d820a 100644
--- a/plugins/available/browser.plugin.bash
+++ b/plugins/available/browser.plugin.bash
@@ -1,42 +1,34 @@
 # based on https://gist.github.com/318247
 
-# Usage: browser
-# pipe html to a browser
-# e.g.
-# $ echo "<h1>hi mom!</h1>" | browser
-# $ ron -5 man/rip.5.ron | browser
+cite about-plugin
+about-plugin 'render commandline output in your browser'
 
 function browser() {
+    about 'pipe html to a browser'
+    example '$ echo "<h1>hi mom!</h1>" | browser'
+    example '$ ron -5 man/rip.5.ron | browser'
+    group 'browser'
+
     if [ -t 0 ]; then
         if [ -n "$1" ]; then
             open $1
         else
-            cat <<usage
-Usage: browser
-pipe html to a browser
-
-$ echo '<h1>hi mom!</h1>' | browser
-$ ron -5 man/rip.5.ron | browser
-usage
-
-    fi
+            reference browser
+        fi
 
     else
         f="/tmp/browser.$RANDOM.html"
         cat /dev/stdin > $f
-        open $f 
+        open $f
     fi
 }
 
 
-# pipe hot spicy interwebs into textmate and cleanup!
-#
-# Usage: wmate
-# wget into a pipe into TextMate and force Tidy (you can undo in textmate)
-# e.g.
-# $ wmate google.com
-
 function wmate() {
+    about 'pipe hot spicy interwebs into textmate and cleanup!'
+    example '$ wmate google.com'
+    group 'browser'
+
     if [ -t 0 ]; then
         if [ -n "$1" ]; then
             wget -qO- $1 | /usr/bin/mate
@@ -64,34 +56,21 @@
 EOT`
 
         else
-            cat <<usage
-Usage: wmate google.com
-wget into a pipe into TextMate and force Tidy (you can undo in textmate)
-
-$ wmate google.com
-usage
-
+            reference wmate
       fi
     fi
 }
 
-#
-# Usage: raw google.com
-# wget into a temp file and pump it into your browser
-#
-# e.g.
-# $ raw google.com
 function raw() {
+    about 'write wget into a temp file and pump it into your browser'
+    example '$ raw google.com'
+    group 'browser'
+
     if [ -t 0 ]; then
         if [ -n "$1" ]; then
             wget -qO- $1 | browser
         else
-            cat <<usage
-Usage: raw google.com
-wget into a temp file and pump it into your browser
-
-$ raw google.com
-usage
-      fi
+            reference raw
+        fi
     fi
 }
diff --git a/plugins/available/dirs.plugins.bash b/plugins/available/dirs.plugin.bash
old mode 100755
new mode 100644
similarity index 85%
rename from plugins/available/dirs.plugins.bash
rename to plugins/available/dirs.plugin.bash
index e03ccf5..bb1fb02
--- a/plugins/available/dirs.plugins.bash
+++ b/plugins/available/dirs.plugin.bash
@@ -1,5 +1,3 @@
-#!/usr/bin/env bash
-
 # Directory stack navigation:
 #
 # Add to stack with: pu /path/to/directory
@@ -7,6 +5,9 @@
 # Show stack with: d
 # Jump to location by number.
 
+cite about-plugin
+about-plugin 'directory stack navigation'
+
 # Show directory stack
 alias d="dirs -v -l"
 
@@ -31,6 +32,9 @@
 alias po="popd"
 
 function dirs-help() {
+  about 'directory navigation alias usage'
+  group 'dirs'
+
   echo "Directory Navigation Alias Usage"
   echo
   echo "Use the power of directory stacking to move"
@@ -64,10 +68,18 @@
 alias L='cat ~/.dirs'
 
 G () {				# goes to distination dir otherwise , stay in the dir
+    about 'goes to destination dir'
+    param '1: directory'
+    example '$ G ..'
+    group 'dirs'
+
     cd ${1:-$(pwd)} ;
 }
 
 S () {				# SAVE a BOOKMARK
+    about 'save a bookmark'
+    group 'dirs'
+
     sed "/$@/d" ~/.dirs > ~/.dirs1;
     \mv ~/.dirs1 ~/.dirs;
     echo "$@"=\"`pwd`\" >> ~/.dirs;
@@ -75,6 +87,9 @@
 }
 
 R () {				# remove a BOOKMARK
+    about 'remove a bookmark'
+    group 'dirs'
+
     sed "/$@/d" ~/.dirs > ~/.dirs1;
     \mv ~/.dirs1 ~/.dirs;
 }
diff --git a/plugins/available/extract.plugin.bash b/plugins/available/extract.plugin.bash
index 1c3e9b1..bb52045 100644
--- a/plugins/available/extract.plugin.bash
+++ b/plugins/available/extract.plugin.bash
@@ -1,3 +1,5 @@
+cite about-plugin
+about-plugin 'one command to extract them all...'
 extract () {
   if [ $# -ne 1 ]
   then
diff --git a/plugins/available/fasd.plugin.bash b/plugins/available/fasd.plugin.bash
new file mode 100755
index 0000000..ee810e2
--- /dev/null
+++ b/plugins/available/fasd.plugin.bash
@@ -0,0 +1,598 @@
+#!/usr/bin/env sh
+
+# Fasd (this file) can be sourced or executed by any POSIX compatible shell.
+
+# Fasd is originally written based on code from z (https://github.com/rupa/z)
+# by rupa deadwyler under the WTFPL license. Most if not all of the code has
+# been rewritten.
+
+# Copyright (C) 2011, 2012 by Wei Dai. All rights reserved.
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+fasd() {
+
+  case "$1" in
+  --init)
+    shift
+    while [ "$1" ]; do
+      case $1 in
+        env)
+          { # source rc files if present
+          [ -s "/etc/fasdrc" ] && . "/etc/fasdrc"
+          [ -s "$HOME/.fasdrc" ] && . "$HOME/.fasdrc"
+
+          # set default options
+          [ -z "$_FASD_DATA" ] && _FASD_DATA="$HOME/.fasd"
+          [ -z "$_FASD_BLACKLIST" ] && _FASD_BLACKLIST="--help"
+          [ -z "$_FASD_SHIFT" ] && _FASD_SHIFT="sudo busybox"
+          [ -z "$_FASD_IGNORE" ] && _FASD_IGNORE="fasd cd ls echo"
+          [ -z "$_FASD_SINK" ] && _FASD_SINK=/dev/null
+          [ -z "$_FASD_TRACK_PWD" ] && _FASD_TRACK_PWD=1
+          [ -z "$_FASD_MAX" ] && _FASD_MAX=2000
+          [ -z "$_FASD_BACKENDS" ] && _FASD_BACKENDS=native
+
+          if [ -z "$_FASD_AWK" ]; then
+            # awk preferences
+            local awk; for awk in mawk gawk original-awk nawk awk; do
+              $awk "" && _FASD_AWK=$awk && break
+            done
+          fi
+        } >> "${_FASD_SINK:-/dev/null}" 2>&1
+        ;;
+
+      auto) cat <<EOS
+{ if compctl; then # zsh
+    eval "\$(fasd --init posix-alias zsh-hook zsh-ccomp zsh-ccomp-install \
+      zsh-wcomp zsh-wcomp-install)"
+  elif complete; then # bash
+    eval "\$(fasd --init posix-alias bash-hook bash-ccomp bash-ccomp-install)"
+  else # posix shell
+    eval "\$(fasd --init posix-alias posix-hook)"
+  fi
+} >> "$_FASD_SINK" 2>&1
+
+EOS
+        ;;
+
+      posix-alias) cat <<EOS
+alias a='fasd -a'
+alias s='fasd -si'
+alias sd='fasd -sid'
+alias sf='fasd -sif'
+alias d='fasd -d'
+alias f='fasd -f'
+# function to execute built-in cd
+fasd_cd() { [ \$# -gt 1 ] && cd "\$(fasd -e echo "\$@")" || fasd "\$@"; }
+alias z='fasd_cd -d'
+
+EOS
+        ;;
+
+      zsh-hook) cat <<EOS
+# add zsh hook
+_fasd_preexec() {
+  { eval "fasd --proc \$(fasd --sanitize \$3)"; } >> "$_FASD_SINK" 2>&1
+}
+autoload -U add-zsh-hook
+add-zsh-hook preexec _fasd_preexec
+
+EOS
+        ;;
+
+      bash-hook) cat <<EOS
+# add bash hook
+echo \$PROMPT_COMMAND | grep -v -q "fasd --proc" && \
+  PROMPT_COMMAND='eval "fasd --proc \$(fasd --sanitize \$(history 1 | \
+  sed -e "s/^[ ]*[0-9]*[ ]*//"))" >> "$_FASD_SINK" 2>&1;'"\$PROMPT_COMMAND"
+
+EOS
+        ;;
+
+      posix-hook) cat <<EOS
+_fasd_ps1_func() {
+  { eval "fasd --proc \$(fasd --sanitize \
+      \$(fc -nl -0 | sed -n '\$s/\s*\(.*\)/\1/p'))"; } >> "$_FASD_SINK" 2>&1
+}
+echo "\$PS1" | grep -v -q "_fasd_ps1_func" && \
+export PS1="\\\$(_fasd_ps1_func)\$PS1"
+
+EOS
+        ;;
+
+      zsh-ccomp) cat <<EOS
+# zsh command mode completion
+_fasd_zsh_cmd_complete() {
+  local compl
+  read -c compl
+  compstate[insert]=menu # no expand
+  reply=(\${(f)"\$(fasd --complete "\$compl")"})
+}
+
+EOS
+        ;;
+
+      zsh-wcomp) cat <<EOS
+# zsh word mode completion
+_fasd_zsh_word_complete() {
+  [ "\$2" ] && local _fasd_cur="\$2"
+  [ -z "\$_fasd_cur" ] && local _fasd_cur="\${words[CURRENT]}"
+  local fnd="\${_fasd_cur//,/ }"
+  local typ=\${1:-e}
+  fasd --query \$typ \$fnd | sort -nr | sed 's/^[0-9.]*[ ]*//' | \
+    while read line; do
+      compadd -U -V fasd "\$line"
+    done
+  compstate[insert]=menu # no expand
+}
+_fasd_zsh_word_complete_f() { _fasd_zsh_word_complete f ; }
+_fasd_zsh_word_complete_d() { _fasd_zsh_word_complete d ; }
+_fasd_zsh_word_complete_trigger() {
+  local _fasd_cur="\${words[CURRENT]}"
+  eval \$(fasd --word-complete-trigger _fasd_zsh_word_complete \$_fasd_cur)
+}
+# define zle widgets
+zle -C fasd-complete 'menu-select' _fasd_zsh_word_complete
+zle -C fasd-complete-f 'menu-select' _fasd_zsh_word_complete_f
+zle -C fasd-complete-d 'menu-select' _fasd_zsh_word_complete_d
+
+EOS
+        ;;
+
+      zsh-ccomp-install) cat <<EOS
+# enbale command mode completion
+compctl -U -K _fasd_zsh_cmd_complete -V fasd -x 'C[-1,-*e],s[-]n[1,e]' -c - \
+  'c[-1,-A][-1,-D]' -f -- fasd fasd_cd
+
+EOS
+        ;;
+
+      zsh-wcomp-install) cat <<EOS
+# enable word mode completion
+zstyle ':completion:*' completer _complete _ignored \
+  _fasd_zsh_word_complete_trigger
+
+EOS
+        ;;
+
+      bash-ccomp) cat <<EOS
+# bash command mode completion
+_fasd_bash_cmd_complete() {
+  # complete command after "-e"
+  local cur=\${COMP_WORDS[COMP_CWORD]}
+  [[ \${COMP_WORDS[COMP_CWORD-1]} == -*e ]] && \
+    COMPREPLY=( \$(compgen -A command \$cur) ) && return
+  # complete using default readline complete after "-A" or "-D"
+  case \${COMP_WORDS[COMP_CWORD-1]} in
+    -A|-D) COMPREPLY=( \$(compgen -o default \$cur) ) && return
+  esac
+  # get completion results using expanded aliases
+  local RESULT=\$( fasd -q --complete "\$(alias -p \$COMP_WORDS \
+    2>> "$_FASD_SINK" | sed -n "\\\$s/^.*'\(.*\)'/\1/p") \${COMP_LINE#* }" )
+  IFS=\$'\n' COMPREPLY=( \$RESULT )
+}
+_fasd_bash_hook_cmd_complete() {
+  for cmd in \$*; do
+    complete -F _fasd_bash_cmd_complete \$cmd
+  done
+}
+
+EOS
+        ;;
+
+      bash-wcomp) cat <<EOS
+# bash word mode completion
+_fasd_bash_word_complete() {
+  [ "\$2" ] && local _fasd_cur="\$2"
+  [ "\$_fasd_cur" ] || local _fasd_cur="\${COMP_WORDS[COMP_CWORD]}"
+  local typ=\${1:-e}
+  local fnd="\${_fasd_cur//,/ }"
+  local RESULT=\$(fasd -q --query \$typ \$fnd | sed 's/^[0-9.]*[ ]*//')
+  IFS=\$'\n' COMPREPLY=( \$RESULT )
+} >> "$_FASD_SINK" 2>&1
+_fasd_bash_word_complete_trigger() {
+  [ "\$_fasd_cur" ] || local _fasd_cur="\${COMP_WORDS[COMP_CWORD]}"
+  eval "\$(fasd --word-complete-trigger _fasd_bash_word_complete \$_fasd_cur)"
+} >> "$_FASD_SINK" 2>&1
+_fasd_bash_word_complete_wrap() {
+  local _fasd_cur="\${COMP_WORDS[COMP_CWORD]}"
+  _fasd_bash_word_complete_trigger
+  local z=\${COMP_WORDS[0]}
+  # try original comp func
+  [ "\$COMPREPLY" ] || eval "\$( echo "\$_FASD_BASH_COMPLETE_P" | \
+    sed -n "/ \$z\$/"'s/.*-F \(.*\) .*/\1/p' )"
+  # fall back on original complete options
+  local cmd="\$(echo "\$_FASD_BASH_COMPLETE_P" | \
+    sed -n "/ \$z\$/"'s/complete/compgen/') \$_fasd_cur"
+  [ "\$COMPREPLY" ] || COMPREPLY=( \$(eval \$cmd) )
+} >> "$_FASD_SINK" 2>&1
+
+EOS
+        ;;
+
+      bash-ccomp-install) cat <<EOS
+# enable bash command mode completion
+_fasd_bash_hook_cmd_complete fasd a s d f sd sf z
+
+EOS
+        ;;
+
+      bash-wcomp-install) cat <<EOS
+_FASD_BASH_COMPLETE_P="\$(complete -p)"
+for cmd in \$(complete -p | awk '{print \$NF}' | tr '\n' ' '); do
+  complete -o default -o bashdefault -F _fasd_bash_word_complete_wrap \$cmd
+done
+# enable word mode completion as default completion
+complete -o default -o bashdefault -D -F _fasd_bash_word_complete_trigger \
+  >> "$_FASD_SINK" 2>&1
+
+EOS
+        ;;
+      esac; shift
+    done
+    ;;
+
+  --init-alias)
+    fasd --init posix-alias
+    ;;
+
+  --init-zsh)
+    fasd --init zsh-hook zsh-ccomp zsh-ccomp-install zsh-wcomp zsh-wcomp-install
+    ;;
+
+  --init-bash)
+    fasd --init bash-hook bash-ccomp bash-ccomp-install
+    ;;
+
+  --init-posix)
+    fasd --init posix-hook
+    ;;
+
+  # if "$_fasd_cur" is a query, then eval all the arguments
+  --word-complete-trigger)
+    shift; [ "$2" ] && local _fasd_cur="$2" || return
+    case "$_fasd_cur" in
+      ,*) echo "$1" e "$_fasd_cur";;
+      f,*) echo "$1" f "${_fasd_cur#?}";;
+      d,*) echo "$1" d "${_fasd_cur#?}";;
+      *,,) echo "$1" e "$_fasd_cur";;
+      *,,f) echo "$1" f "${_fasd_cur%?}";;
+      *,,d) echo "$1" d "${_fasd_cur%?}";;
+    esac
+    ;;
+
+  --sanitize)
+    shift; echo "$@" | \
+      sed 's/\([^\]\)$([^ ]*\([^)]*\)))*/\1\2/g;s/\([^\]\)[|&;<>$`]\{1,\}/\1 /g'
+    ;;
+
+  --proc) shift # process commands
+    # stop if we don't own ~/.fasd (we're another user but our ENV is still set)
+    [ -f "$_FASD_DATA" -a ! -O "$_FASD_DATA" ] && return
+
+    # make zsh do word splitting for the for loop to work
+    [ "$ZSH_VERSION" ] && emulate sh && setopt localoptions
+
+    # blacklists
+    local each; for each in $_FASD_BLACKLIST; do
+      case " $* " in *\ $each\ *) return;; esac
+    done
+
+    # shifts
+    while true; do
+      case " $_FASD_SHIFT " in
+        *\ $1\ *) shift;;
+        *) break
+      esac
+    done
+
+    # ignores
+    case " $_FASD_IGNORE " in
+      *\ $1\ *) return
+    esac
+
+    shift; fasd --add "$@" # add all arguments except command
+    ;;
+
+  --add|-A) shift # add entries
+    # find all valid path arguments, convert them to simplest absolute form
+    local paths="$(while [ "$1" ]; do
+      [ -e "$1" ] && echo "$1"; shift
+    done | sed '/^[^/]/s@^@'"$PWD"'/@
+      s@/\.\.$@/\.\./@;s@/\(\./\)\{1,\}@/@g;: 0;s@[^/][^/]*//*\.\./@/@;t 0
+      s@^/*\.\./@/@;s@//*@/@g;s@/\.\{0,1\}$@@;s@^$@/@' | tr '\n' '|')"
+
+    # add current pwd if the option is set
+    [ "$_FASD_TRACK_PWD" = "1" -a "$PWD" != "$HOME" ] && paths="$paths|$PWD"
+
+    [ -z "${paths##\|}" ] && return # stop if we have nothing to add
+
+    # maintain the file
+    local tempfile
+    tempfile="$(mktemp "$_FASD_DATA".XXXXXX)" || return
+    $_FASD_AWK -v list="$paths" -v now="$(date +%s)" -v max="$_FASD_MAX" -F"|" '
+      BEGIN {
+        split(list, files, "|")
+        for(i in files) {
+          path = files[i]
+          if(path == "") continue
+          paths[path] = path # array for checking
+          rank[path] = 1
+          time[path] = now
+        }
+      }
+      $2 >= 1 {
+        if($1 in paths) {
+          rank[$1] = $2 + 1
+          time[$1] = now
+        } else {
+          rank[$1] = $2
+          time[$1] = $3
+        }
+        count += $2
+      }
+      END {
+        if(count > max)
+          for(i in rank) print i "|" 0.9*rank[i] "|" time[i] # aging
+        else
+          for(i in rank) print i "|" rank[i] "|" time[i]
+      }' "$_FASD_DATA" 2>> "$_FASD_SINK" >| "$tempfile"
+    if [ $? -ne 0 -a -f "$_FASD_DATA" ]; then
+      env rm -f "$tempfile"
+    else
+      env mv -f "$tempfile" "$_FASD_DATA"
+    fi
+    ;;
+
+  --delete|-D) shift # delete entries
+    # turn valid arguments into entry-deleting sed commands
+    local sed_cmd="$(while [ "$1" ]; do echo "$1"; shift; done | \
+      sed '/^[^/]/s@^@'"$PWD"'/@;s@/\.\.$@/\.\./@;s@/\(\./\)\{1,\}@/@g
+        : 0;s@[^/][^/]*//*\.\./@/@;t 0;s@^/*\.\./@/@;s@//*@/@g;s@/\.\{0,1\}$@@
+        s@^$@/@;s@\([.[/*^$]\)@\\\1@g;s@^\(.*\)$@/^\1|/d@')"
+
+    # maintain the file
+    local tempfile
+    tempfile="$(mktemp "$_FASD_DATA".XXXXXX)" || return
+
+    sed -e "$sed_cmd" "$_FASD_DATA" 2>> "$_FASD_SINK" >| "$tempfile"
+
+    if [ $? -ne 0 -a -f "$_FASD_DATA" ]; then
+      env rm -f "$tempfile"
+    else
+      env mv -f "$tempfile" "$_FASD_DATA"
+    fi
+    ;;
+
+  --query) shift # query the db, --query [$typ ["$fnd" [$mode [$quote]]]]
+    [ -f "$_FASD_DATA" ] || return # no db yet
+    [ "$1" ] && local typ="$1"
+    [ "$2" ] && local fnd="$2"
+    [ "$3" ] && local mode="$3"
+    [ "$4" ] && local quote="$4"
+    [ "$quote" ] && local qts='"\""' || local qts=
+
+    # make zsh do word spliting for the for loop to work
+    [ "$ZSH_VERSION" ] && emulate sh && setopt localoptions
+
+    # cat all backends
+    local each _fasd_data; for each in $_FASD_BACKENDS; do
+      _fasd_data="$_fasd_data
+$(fasd --backend $each)"
+    done
+    [ "$_fasd_data" ] || _fasd_data="$(cat "$_FASD_DATA")"
+
+    # set mode specific code for calculating the prior
+    case $mode in
+      rank) local prior='times[i]';;
+      recent) local prior='sqrt(100000/(1+t-la[i]))';;
+      *) local prior='times[i] * frecent(la[i])';;
+    esac
+
+    # query the database
+    echo "$_fasd_data" | while read line; do
+      [ -${typ:-e} "${line%%\|*}" ] && echo "$line"
+    done | $_FASD_AWK -v t="$(date +%s)" -v q="$fnd" -F"|" '
+      function frecent(time) {
+        dx = t-time
+        if( dx < 3600 ) return 6
+        if( dx < 86400 ) return 4
+        if( dx < 604800 ) return 2
+        return 1
+      }
+      function likelihood(pattern, path) {
+        m = gsub("/+", "/", path)
+        r = 1
+        for(i in pattern) {
+          tmp = path
+          gsub(".*" pattern[i], "", tmp)
+          n = gsub("/+", "/", tmp)
+          if(n == m)
+            return 0
+          else if(n == 0)
+            r *= 20 # F
+          else
+            r *= 1 - (n / m)
+        }
+        return r
+      }
+      BEGIN {
+        split(q, pattern, " ")
+        for(i in pattern) pattern_lower[i] = tolower(pattern[i]) # nocase
+      }
+      {
+        if(!wcase[$1]) {
+          times[$1] = $2
+          la[$1] = $3
+          wcase[$1] = likelihood(pattern, $1)
+          if(!cx) nocase[$1] = likelihood(pattern_lower, tolower($1))
+        } else {
+          times[$1] += $2
+          if($3 > la[$1]) la[$1] = $3
+        }
+        cx = cx || wcase[$1]
+        ncx = ncx || nocase[$1]
+      }
+      END {
+        if(cx) {
+          for(i in wcase) {
+            if(wcase[i])
+              printf "%-10s %s\n", '"$prior"' * wcase[i], '"$qts"' i '"$qts"'
+          }
+        } else if(ncx) {
+          for(i in nocase) {
+            if(nocase[i])
+              printf "%-10s %s\n", '"$prior"' * nocase[i], '"$qts"' i '"$qts"'
+          }
+        }
+      }' - 2>> "$_FASD_SINK"
+    ;;
+
+  --backend)
+    case $2 in
+      native) cat "$_FASD_DATA";;
+      viminfo)
+        local t="$(date +%s)"
+        < "$HOME/.viminfo" sed -n '/^>/{s@~@'"$HOME"'@;p}' | \
+          while IFS=" " read line; do
+            t=$((t-60)); echo "${line#??}|1|$t"
+          done
+        ;;
+      recently-used)
+        tr -d '\n' < "$HOME/.local/share/recently-used.xbel" | \
+          sed 's@file:/@\n@g;s@count="@\n@g' | sed '1d;s/".*$//' | \
+          tr '\n' '|' | sed 's@|/@\n@g' | $_FASD_AWK -F'|' '{
+            sum = 0
+            for( i=2; i<=NF; i++ ) sum += $i
+            print $1 "|" sum
+          }'
+        ;;
+      *) eval "$2";;
+    esac
+    ;;
+
+  *) # parsing logic and processing
+    local fnd last _FASD_BACKENDS="$_FASD_BACKENDS" _fasd_data
+    while [ "$1" ]; do case "$1" in
+      --complete) [ "$2" = "--" ] && shift; set -- $(echo $2); local lst=1 r=r;;
+      --query|--add|--delete|-A|-D) fasd "$@"; return $?;;
+      --version) echo "0.5.4"; return 0;;
+      --) while [ "$2" ]; do shift; fnd="$fnd$1 "; last="$1"; done;;
+      -*) local o="${1#-}"; while [ "$o" ]; do case "$o" in
+          s*) local show=1;;
+          l*) local lst=1;;
+          i*) local interactive=1 show=1;;
+          r*) local mode=rank;;
+          t*) local mode=recent;;
+          e*) o="${o#?}"; if [ "$o" ]; then # there are characters after "-e"
+                local exec="$o" # anything after "-e"
+              else # use the next argument
+                local exec="${2:?"-e: Argument needed "}"
+                shift
+              fi; break;;
+          b*) o="${o#?}"; if [ "$o" ]; then
+                _FASD_BACKENDS="$o"
+              else
+                _FASD_BACKENDS="${2:?"-b: Argument needed"}"
+                shift
+              fi; break;;
+          B*) o="${o#?}"; if [ "$o" ]; then
+                _FASD_BACKENDS="$_FASD_BACKENDS $o"
+              else
+                _FASD_BACKENDS="$_FASD_BACKENDS ${2:?"-B: Argument needed"}"
+                shift
+              fi; break;;
+          a*) local typ=e;;
+          d*) local typ=d;;
+          f*) local typ=f;;
+          q*) local quote=1;;
+          h*) echo "fasd [options] [query ...]
+[f|a|s|d|z] [opions] [query ...]
+  options:
+    -s         show list of files with their ranks
+    -l         list paths only
+    -i         interactive mode
+    -e <cmd>   set command to execute on the result file
+    -b <name>  only use <name> backend
+    -b <name>  add additional backend <name>
+    -a         match files and directories
+    -d         match directories only
+    -f         match files only
+    -r         match by rank only
+    -t         match by recent access only
+    -h         show a brief help message
+
+fasd [-A|-D] [paths ...]
+    -A    add paths
+    -D    delete paths" >&2; return;;
+        esac; o="${o#?}"; done;;
+      *) fnd="$fnd $1"; last="$1";;
+    esac; shift; done
+
+    # if we hit enter on a completion just execute
+    case "$last" in
+     # completions will always start with /
+     /*) [ -z "$show$lst" -a -${typ:-e} "$last" -a "$exec" ] \
+       && eval $exec "\"$last\"" && return;;
+    esac
+
+    local result
+    result="$(fasd --query 2>> "$_FASD_SINK")" # query the database
+    [ $? -gt 0 ] && return
+    if [ "$interactive" ] || [ "$exec" -a -z "$fnd$lst$show" -a -t 1 ]; then
+      result="$(echo "$result" | sort -nr)"
+      echo "$result" | sed = | sed 'N;s/\n/	/' | sort -nr >&2; printf "> " >&2
+      local i; read i; [ 0 -lt "${i:-0}" ] 2>> "$_FASD_SINK" || return 1
+      result="$(echo "$result" | sed -n "${i:-1}"'s/^[0-9.]*[ ]*//p')"
+      if [ "$result" ]; then
+        fasd --add "$result"; eval ${exec:-echo} "\"$result\""
+      fi
+    elif [ "$lst" ]; then
+      echo "$result" | sort -n${r} | sed 's/^[0-9.]*[ ]*//'
+    elif [ "$show" ]; then
+      echo "$result" | sort -n
+    elif [ "$fnd" -a "$exec" ]; then # exec
+      result="$(echo "$result" | sort -n | sed -n '$s/^[0-9.]*[ ]*//p')"
+      fasd --add "$result"; eval $exec "\"$result\""
+    elif [ "$fnd" -a ! -t 1 ]; then # echo if output is not terminal
+      result="$(echo "$result" | sort -n | sed -n '$s/^[0-9.]*[ ]*//p')"
+      fasd --add "$result"; echo "$result"
+    else # no args, show
+      echo "$result" | sort -n
+    fi
+
+  esac
+}
+
+fasd --init env
+
+case $- in
+  *i*) cite about-plugin
+       about-plugin 'navigate "frecently" used files and directories'
+       eval "$(fasd --init auto)"
+      ;;
+  *) # assume being executed as an executable
+    if [ -x "$_FASD_SHELL" -a -z "$_FASD_SET" ]; then
+      _FASD_SET=1 exec $_FASD_SHELL "$0" "$@"
+    else
+      fasd "$@"
+    fi
+esac
+
diff --git a/plugins/available/git.plugins.bash b/plugins/available/git.plugin.bash
similarity index 83%
rename from plugins/available/git.plugins.bash
rename to plugins/available/git.plugin.bash
index 41b031f..0b77111 100644
--- a/plugins/available/git.plugins.bash
+++ b/plugins/available/git.plugin.bash
@@ -1,26 +1,42 @@
-#!/usr/bin/env bash
+cite about-plugin
+about-plugin 'git helper functions'
 
 function git_remote {
+  about 'adds remote $GIT_HOSTING:$1 to current repo'
+  group 'git'
+
   echo "Running: git remote add origin ${GIT_HOSTING}:$1.git"
   git remote add origin $GIT_HOSTING:$1.git
 }
 
 function git_first_push {
+  about 'push into origin refs/heads/master'
+  group 'git'
+
   echo "Running: git push origin master:refs/heads/master"
   git push origin master:refs/heads/master
 }
 
 function git_remove_missing_files() {
+  about "git rm's missing files"
+  group 'git'
+
   git ls-files -d -z | xargs -0 git update-index --remove
 }
 
 # Adds files to git's exclude file (same as .gitignore)
 function local-ignore() {
+  about 'adds file or path to git exclude file'
+  param '1: file or path fragment to ignore'
+  group 'git'
   echo "$1" >> .git/info/exclude
 }
 
 # get a quick overview for your git repo
 function git_info() {
+    about 'overview for your git repo'
+    group 'git'
+
     if [ -n "$(git symbolic-ref HEAD 2> /dev/null)" ]; then
         # print informations
         echo "git repo overview"
@@ -29,7 +45,7 @@
 
         # print all remotes and thier details
         for remote in $(git remote show); do
-            echo $remote:  
+            echo $remote:
             git remote show $remote
             echo
         done
@@ -43,10 +59,10 @@
         fi
 
         # print at least 5 last log entries
-        echo 
+        echo
         echo "log:"
         git log -5 --oneline
-        echo 
+        echo
 
     else
         echo "you're currently not in a git repository"
@@ -55,6 +71,9 @@
 }
 
 function git_stats {
+    about 'display stats per author'
+    group 'git'
+
 # awesome work from https://github.com/esc/git-stats
 # including some modifications
 
diff --git a/plugins/available/hg.plugin.bash b/plugins/available/hg.plugin.bash
new file mode 100644
index 0000000..020b920
--- /dev/null
+++ b/plugins/available/hg.plugin.bash
@@ -0,0 +1,25 @@
+cite about-plugin
+about-plugin 'hg helper functions'
+
+hg_dirty() {
+    about 'displays dirty status of hg repository'
+    group 'hg'
+
+    hg status --no-color 2> /dev/null \
+    | awk '$1 == "?" { print "?" } $1 != "?" { print "!" }' \
+    | sort | uniq | head -c1
+}
+
+hg_in_repo() {
+    about 'determine if pwd is an hg repo'
+    group 'hg'
+
+    [[ `hg branch 2> /dev/null` ]] && echo 'on '
+}
+
+hg_branch() {
+    about 'display current hg branch'
+    group 'hg'
+
+    hg branch 2> /dev/null
+}
diff --git a/plugins/available/hg.plugins.bash b/plugins/available/hg.plugins.bash
deleted file mode 100644
index 33c81d3..0000000
--- a/plugins/available/hg.plugins.bash
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env bash
-hg_dirty() {
-    hg status --no-color 2> /dev/null \
-    | awk '$1 == "?" { print "?" } $1 != "?" { print "!" }' \
-    | sort | uniq | head -c1
-}
-
-hg_in_repo() {
-    [[ `hg branch 2> /dev/null` ]] && echo 'on '
-}
-
-hg_branch() {
-    hg branch 2> /dev/null
-}
diff --git a/plugins/available/java.plugin.bash b/plugins/available/java.plugin.bash
new file mode 100644
index 0000000..98e4624
--- /dev/null
+++ b/plugins/available/java.plugin.bash
@@ -0,0 +1,11 @@
+cite about-plugin
+about-plugin 'Java and JAR helper functions'
+
+function jar_manifest {
+  about "extracts the specified JAR file's MANIFEST file and prints it to stdout"
+  group 'java'
+  param '1: JAR file to extract the MANIFEST from'
+  example 'jar_manifest lib/foo.jar'
+
+  unzip -c $1 META-INF/MANIFEST.MF
+}
diff --git a/plugins/available/javascript.plugins.bash b/plugins/available/javascript.plugin.bash
similarity index 67%
rename from plugins/available/javascript.plugins.bash
rename to plugins/available/javascript.plugin.bash
index 76a7e13..e8037b9 100644
--- a/plugins/available/javascript.plugins.bash
+++ b/plugins/available/javascript.plugin.bash
@@ -1,16 +1,22 @@
-#!/usr/bin/env bash
-#
-# The install directory is hard-coded. TOOD: allow the directory to be specified on the command line.
-#
+# The install directory is hard-coded. TODO: allow the directory to be specified on the command line.
+
+cite about-plugin
+about-plugin 'download jquery files into current project'
 
 [[ -z "$JQUERY_VERSION_NUMBER" ]] && JQUERY_VERSION_NUMBER="1.6.1"
 [[ -z "$JQUERY_UI_VERSION_NUMBER" ]] && JQUERY_UI_VERSION_NUMBER="1.8.13"
 
 function rails_jquery {
+  about 'download rails.js into public/javascripts'
+  group 'javascript'
+
   curl -o public/javascripts/rails.js http://github.com/rails/jquery-ujs/raw/master/src/rails.js
 }
 
 function jquery_install {
+  about 'download jquery.js into public/javascripts'
+  group 'javascript'
+
   if [ -z "$1" ]
   then
       version=$JQUERY_VERSION_NUMBER
@@ -21,6 +27,9 @@
 }
 
 function jquery_ui_install {
+  about 'download jquery_us.js into public/javascripts'
+  group 'javascript'
+
   if [ -z "$1" ]
   then
       version=$JQUERY_UI_VERSION_NUMBER
diff --git a/plugins/available/jekyll.plugins.bash b/plugins/available/jekyll.plugin.bash
similarity index 92%
rename from plugins/available/jekyll.plugins.bash
rename to plugins/available/jekyll.plugin.bash
index 74e3cb1..6254a87 100644
--- a/plugins/available/jekyll.plugins.bash
+++ b/plugins/available/jekyll.plugin.bash
@@ -1,6 +1,11 @@
-#!/usr/bin/env bash
+cite about-plugin
+about-plugin 'manage your jekyll site'
 
 editpost() {
+  about 'edit a post'
+  param '1: site directory'
+  group 'jekyll'
+
   unset SITE
   if [ -z "$1" ]
   then
@@ -35,11 +40,11 @@
     DATE=`echo $POST | grep -oE "[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}"`
     TITLE=`cat $POST | grep -oE "title: (.+)"`
     TITLE=`echo $TITLE | sed 's/title: //'`
-    echo "$COUNTER) 	$DATE	$TITLE" >> "$TMPFILE"	
+    echo "$COUNTER) 	$DATE	$TITLE" >> "$TMPFILE"
     POSTS[$COUNTER]=$POST
     COUNTER=`expr $COUNTER + 1`
   done
-  less $TMPFILE	
+  less $TMPFILE
   read -p "Number of post to edit: " POST_TO_EDIT
   if [ -z "$JEKYLL_EDITOR" ]
   then
@@ -50,6 +55,10 @@
 }
 
 newpost() {
+  about 'create a new post'
+  param '1: site directory'
+  group 'jekyll'
+
   unset SITE
   if [ -z "$1" ]
   then
@@ -93,7 +102,7 @@
   then
     select OPTION in $OPTIONS
     do
-      if [[ $OPTION = "Text" ]] 
+      if [[ $OPTION = "Text" ]]
       then
         POST_TYPE="Text"
         break
@@ -257,6 +266,10 @@
 }
 
 function testsite() {
+  about 'launches local jekyll server'
+  param '1: site directory'
+  group 'jekyll'
+
   unset SITE
   if [ -z "$1" ]
   then
@@ -285,6 +298,10 @@
 }
 
 function buildsite() {
+  about 'builds site'
+  param '1: site directory'
+  group 'jekyll'
+
   unset SITE
   if [ -z "$1" ]
   then
@@ -314,6 +331,10 @@
 }
 
 function deploysite() {
+  about 'rsyncs site to remote host'
+  param '1: site directory'
+  group 'jekyll'
+
   unset SITE
   if [ -z "$1" ]
   then
diff --git a/plugins/available/latex.plugin.bash b/plugins/available/latex.plugin.bash
index c2ce9dd..eefec59 100644
--- a/plugins/available/latex.plugin.bash
+++ b/plugins/available/latex.plugin.bash
@@ -1,4 +1,5 @@
-#!/usr/bin/env bash
+cite about-plugin
+about-plugin 'use mactex'
 
 # add mactex to the path if its present
 MACTEX_PATH=/usr/local/texlive/2009/bin/universal-darwin
diff --git a/plugins/available/nginx.plugins.bash b/plugins/available/nginx.plugin.bash
similarity index 81%
rename from plugins/available/nginx.plugins.bash
rename to plugins/available/nginx.plugin.bash
index 6dd86dd..660b503 100644
--- a/plugins/available/nginx.plugins.bash
+++ b/plugins/available/nginx.plugin.bash
@@ -1,6 +1,10 @@
-#!/usr/bin/env bash
+cite about-plugin
+about-plugin 'manage your nginx service'
 
 function nginx_reload() {
+  about 'reload your nginx config'
+  group 'nginx'
+
   FILE="${NGINX_PATH}/logs/nginx.pid"
   if [ -e $FILE ]; then
     echo "Reloading NGINX..."
@@ -13,6 +17,9 @@
 }
 
 function nginx_stop() {
+  about 'stop nginx'
+  group 'nginx'
+
   FILE="${NGINX_PATH}/logs/nginx.pid"
   if [ -e $FILE ]; then
     echo "Stopping NGINX..."
@@ -25,6 +32,9 @@
 }
 
 function nginx_start() {
+  about 'start nginx'
+  group 'nginx'
+
   FILE="${NGINX_PATH}/sbin/nginx"
   if [ -e $FILE ]; then
     echo "Starting NGINX..."
@@ -35,6 +45,9 @@
 }
 
 function nginx_restart() {
+  about 'restart nginx'
+  group 'nginx'
+
   FILE="${NGINX_PATH}/logs/nginx.pid"
   if [ -e $FILE ]; then
     echo "Stopping NGINX..."
diff --git a/plugins/available/nvm.plugin.bash b/plugins/available/nvm.plugin.bash
index 3f489dd..2e607d6 100644
--- a/plugins/available/nvm.plugin.bash
+++ b/plugins/available/nvm.plugin.bash
@@ -5,6 +5,9 @@
 # Implemented by Tim Caswell <tim@creationix.com>
 # with much bash help from Matthew Ranney
 
+cite about-plugin
+about-plugin 'node version manager, as a bash function'
+
 export NVM_DIR=$HOME/.nvm
 
 if [ ! -d "$NVM_DIR" ]; then
@@ -75,6 +78,10 @@
 
 nvm()
 {
+  about 'Node Version Manager'
+  param '1: command, see nvm help'
+  group 'nvm'
+
   if [ $# -lt 1 ]; then
     nvm help
     return
diff --git a/plugins/available/osx.plugin.bash b/plugins/available/osx.plugin.bash
index 7d3d077..fde9c83 100644
--- a/plugins/available/osx.plugin.bash
+++ b/plugins/available/osx.plugin.bash
@@ -1,6 +1,10 @@
-#!/usr/bin/env bash
+cite about-plugin
+about-plugin 'osx-specific functions'
 
 function tab() {
+  about 'opens a new terminal tab'
+  group 'osx'
+
   osascript 2>/dev/null <<EOF
     tell application "System Events"
       tell process "Terminal" to keystroke "t" using command down
@@ -15,6 +19,10 @@
 # this one switches your os x dock between 2d and 3d
 # thanks to savier.zwetschge.org
 function dock-switch() {
+    about 'switch dock between 2d and 3d'
+    param '1: "2d" or "3d"'
+    example '$ dock-switch 2d'
+    group 'osx'
 
     if [ $(uname) = "Darwin" ]; then
 
@@ -39,6 +47,10 @@
 # Download a file and open it in Preview
 
 function prevcurl() {
+  about 'download a file and open it in Preview'
+  param '1: url'
+  group 'osx'
+
   if [ ! $(uname) = "Darwin" ]
   then
     echo "This function only works with Mac OS X"
diff --git a/plugins/available/python.plugin.bash b/plugins/available/python.plugin.bash
index 6c87f8a..c705d5b 100644
--- a/plugins/available/python.plugin.bash
+++ b/plugins/available/python.plugin.bash
@@ -1,4 +1,5 @@
-#!/usr/bin/env bash
+cite about-plugin
+about-plugin 'alias "http" to SimpleHTTPServer'
 
 if [ $(uname) = "Linux" ]
 then
diff --git a/plugins/available/rbenv.plugin.bash b/plugins/available/rbenv.plugin.bash
index bd3f78e..70fe62c 100644
--- a/plugins/available/rbenv.plugin.bash
+++ b/plugins/available/rbenv.plugin.bash
@@ -1,8 +1,10 @@
-#!/usr/bin/env bash
-
 # Load rbebv, if you are using it
+
+cite about-plugin
+about-plugin 'load rbenv, if you are using it'
+
 export PATH="$HOME/.rbenv/bin:$PATH"
-eval "$(rbenv init -)"
+[[ `which rbenv` ]] && eval "$(rbenv init -)"
 
 # Load the auto-completion script if rbenv was loaded.
-source ~/.rbenv/completions/rbenv.bash
+[[ -e ~/.rbenv/completions/rbenv.bash ]] && source ~/.rbenv/completions/rbenv.bash
diff --git a/plugins/available/ruby.plugin.bash b/plugins/available/ruby.plugin.bash
index 9ae61a4..f36bb15 100644
--- a/plugins/available/ruby.plugin.bash
+++ b/plugins/available/ruby.plugin.bash
@@ -1,4 +1,10 @@
-#!/usr/bin/env bash
+cite about-plugin
+about-plugin 'adds "remove_gem" function'
+
 function remove_gem {
+  about 'removes installed gem'
+  param '1: installed gem name'
+  group 'ruby'
+
   gem list | grep $1 | awk '{ print $1; }' | xargs sudo gem uninstall
 }
diff --git a/plugins/available/rvm.plugin.bash b/plugins/available/rvm.plugin.bash
index e0d18ba..6acad07 100644
--- a/plugins/available/rvm.plugin.bash
+++ b/plugins/available/rvm.plugin.bash
@@ -1,6 +1,8 @@
-#!/usr/bin/env bash
-
 # Load RVM, if you are using it
+
+cite about-plugin
+about-plugin 'load rvm, if you are using it'
+
 [[ -s $HOME/.rvm/scripts/rvm ]] && source $HOME/.rvm/scripts/rvm
 
 # Check to make sure that RVM is actually loaded before adding
diff --git a/plugins/available/ssh.plugin.bash b/plugins/available/ssh.plugin.bash
new file mode 100644
index 0000000..4e17206
--- /dev/null
+++ b/plugins/available/ssh.plugin.bash
@@ -0,0 +1,19 @@
+cite about-plugin
+about-plugin 'ssh helper functions'
+
+function add_ssh() {
+  about 'add entry to ssh config'
+  param '1: host'
+  param '2: hostname'
+  param '3: user'
+  group 'ssh'
+
+  echo -en "\n\nHost $1\n  HostName $2\n  User $3\n  ServerAliveInterval 30\n  ServerAliveCountMax 120" >> ~/.ssh/config
+}
+
+function sshlist() {
+  about 'list hosts defined in ssh config'
+  group 'ssh'
+
+  awk '$1 ~ /Host$/ { print $2 }' ~/.ssh/config
+}
diff --git a/plugins/available/ssh.plugins.bash b/plugins/available/ssh.plugins.bash
deleted file mode 100644
index 39718fb..0000000
--- a/plugins/available/ssh.plugins.bash
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/usr/bin/env bash
-
-function add_ssh() {
-  echo -en "\n\nHost $1\n  HostName $2\n  User $3\n  ServerAliveInterval 30\n  ServerAliveCountMax 120" >> ~/.ssh/config
-}
-
-function sshlist() {
-  awk '$1 ~ /Host$/ { print $2 }' ~/.ssh/config
-}
diff --git a/plugins/available/subversion.plugin.bash b/plugins/available/subversion.plugin.bash
index a7fe941..bdb3edf 100644
--- a/plugins/available/subversion.plugin.bash
+++ b/plugins/available/subversion.plugin.bash
@@ -1,8 +1,21 @@
-#!/usr/bin/env bash
+cite about-plugin
+about-plugin 'svn helper functions'
+
 rm_svn(){
+  about 'remove ".svn" files from directory'
+  param '1: directory to search for files'
+  group 'svn'
+
+  if [ -z "$1" ]; then
+      reference rm_svn
+      return
+  fi
   find $1 -name .svn -print0 | xargs -0 rm -rf
 }
 
 svn_add(){
-	svn status | grep '^\?' | sed -e 's/? *//' | sed -e 's/ /\ /g' | xargs svn add
+    about 'add to svn repo'
+    group 'svn'
+
+    svn status | grep '^\?' | sed -e 's/? *//' | sed -e 's/ /\ /g' | xargs svn add
 }
diff --git a/plugins/available/tmux.plugin.bash b/plugins/available/tmux.plugin.bash
index ff35336..f7ee337 100644
--- a/plugins/available/tmux.plugin.bash
+++ b/plugins/available/tmux.plugin.bash
@@ -1,2 +1,6 @@
 # make sure that tmux is launched in 256 color mode
+
+cite about-plugin
+about-plugin 'make sure that tmux is launched in 256 color mode'
+
 alias tmux="TERM=xterm-256color tmux"
diff --git a/plugins/available/tmuxinator.plugin.bash b/plugins/available/tmuxinator.plugin.bash
index cf5500d..a011435 100644
--- a/plugins/available/tmuxinator.plugin.bash
+++ b/plugins/available/tmuxinator.plugin.bash
@@ -1,3 +1,4 @@
-#!/usr/bin/env bash
+cite about-plugin
+about-plugin 'sources tmuxinator script if available'
 
 [[ -s $HOME/.tmuxinator/scripts/tmuxinator ]] && . $HOME/.tmuxinator/scripts/tmuxinator
diff --git a/plugins/available/todo.plugin.bash b/plugins/available/todo.plugin.bash
new file mode 100755
index 0000000..28559de
--- /dev/null
+++ b/plugins/available/todo.plugin.bash
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# you may override any of the exported variables below in your .bash_profile
+
+if [ -z "$TODO_DIR" ]; then
+    export TODO_DIR=$BASH_IT/custom  # store todo items in user's custom dir, ignored by git
+fi
+if [ -z "$TODOTXT_DEFAULT_ACTION" ]; then
+    export TODOTXT_DEFAULT_ACTION=ls       # typing 't' by itself will list current todos
+fi
+if [ -z "$TODO_SRC_DIR" ]; then
+    export TODO_SRC_DIR=$BASH_IT/plugins/available/todo
+fi
+
+# respect ENV var set in .bash_profile, default is 't'
+alias $TODO='$TODO_SRC_DIR/todo.sh -d $TODO_SRC_DIR/todo.cfg'
+
+export PATH=$PATH:$TODO_SRC_DIR
+source $TODO_SRC_DIR/todo_completion   # bash completion for todo.sh
+complete -F _todo $TODO                # enable completion for 't' alias
diff --git a/plugins/available/todo/todo.cfg b/plugins/available/todo/todo.cfg
new file mode 100644
index 0000000..d79bf77
--- /dev/null
+++ b/plugins/available/todo/todo.cfg
@@ -0,0 +1,75 @@
+# === EDIT FILE LOCATIONS BELOW ===
+
+# Your todo/done/report.txt locations
+export TODO_FILE="$TODO_DIR/todo.txt"
+export DONE_FILE="$TODO_DIR/done.txt"
+export REPORT_FILE="$TODO_DIR/report.txt"
+
+# You can customize your actions directory location
+#export TODO_ACTIONS_DIR="$HOME/.todo.actions.d"
+
+# == EDIT FILE LOCATIONS ABOVE ===
+
+# === COLOR MAP ===
+
+## Text coloring and formatting is done by inserting ANSI escape codes.
+## If you have re-mapped your color codes, or use the todo.txt
+## output in another output system (like Conky), you may need to
+## over-ride by uncommenting and editing these defaults.
+## If you change any of these here, you also need to uncomment
+## the defaults in the COLORS section below. Otherwise, todo.txt
+## will still use the defaults!
+
+# export BLACK='\\033[0;30m'
+# export RED='\\033[0;31m'
+# export GREEN='\\033[0;32m'
+# export BROWN='\\033[0;33m'
+# export BLUE='\\033[0;34m'
+# export PURPLE='\\033[0;35m'
+# export CYAN='\\033[0;36m'
+# export LIGHT_GREY='\\033[0;37m'
+# export DARK_GREY='\\033[1;30m'
+# export LIGHT_RED='\\033[1;31m'
+# export LIGHT_GREEN='\\033[1;32m'
+# export YELLOW='\\033[1;33m'
+# export LIGHT_BLUE='\\033[1;34m'
+# export LIGHT_PURPLE='\\033[1;35m'
+# export LIGHT_CYAN='\\033[1;36m'
+# export WHITE='\\033[1;37m'
+# export DEFAULT='\\033[0m'
+
+# === COLORS ===
+
+## Uncomment and edit to override these defaults.
+## Reference the constants from the color map above,
+## or use $NONE to disable highlighting.
+#
+# Priorities can be any upper-case letter.
+# A,B,C are highlighted; you can add coloring for more.
+#
+# export PRI_A=$YELLOW        # color for A priority
+# export PRI_B=$GREEN         # color for B priority
+# export PRI_C=$LIGHT_BLUE    # color for C priority
+# export PRI_D=...            # define your own
+# export PRI_X=$WHITE         # color unless explicitly defined
+
+# There is highlighting for tasks that have been done,
+# but haven't been archived yet.
+#
+# export COLOR_DONE=$LIGHT_GREY
+
+# === BEHAVIOR ===
+
+## customize list output
+#
+# TODOTXT_SORT_COMMAND will filter after line numbers are
+# inserted, but before colorization, and before hiding of
+# priority, context, and project.
+#
+# export TODOTXT_SORT_COMMAND='env LC_COLLATE=C sort -f -k2'
+
+# TODOTXT_FINAL_FILTER will filter list output after colorization,
+# priority hiding, context hiding, and project hiding. That is,
+# just before the list output is displayed.
+#
+# export TODOTXT_FINAL_FILTER='cat'
diff --git a/plugins/available/todo/todo.sh b/plugins/available/todo/todo.sh
new file mode 100755
index 0000000..59f88a9
--- /dev/null
+++ b/plugins/available/todo/todo.sh
@@ -0,0 +1,1321 @@
+#! /bin/bash
+
+# === HEAVY LIFTING ===
+shopt -s extglob extquote
+
+# NOTE:  Todo.sh requires the .todo/config configuration file to run.
+# Place the .todo/config file in your home directory or use the -d option for a custom location.
+
+[ -f VERSION-FILE ] && . VERSION-FILE || VERSION="2.9"
+version() {
+    cat <<-EndVersion
+		TODO.TXT Command Line Interface v$VERSION
+
+		First release: 5/11/2006
+		Original conception by: Gina Trapani (http://ginatrapani.org)
+		Contributors: http://github.com/ginatrapani/todo.txt-cli/network
+		License: GPL, http://www.gnu.org/copyleft/gpl.html
+		More information and mailing list at http://todotxt.com
+		Code repository: http://github.com/ginatrapani/todo.txt-cli/tree/master
+	EndVersion
+    exit 1
+}
+
+# Set script name and full path early.
+TODO_SH=$(basename "$0")
+TODO_FULL_SH="$0"
+export TODO_SH TODO_FULL_SH
+
+oneline_usage="$TODO_SH [-fhpantvV] [-d todo_config] action [task_number] [task_description]"
+
+usage()
+{
+    cat <<-EndUsage
+		Usage: $oneline_usage
+		Try '$TODO_SH -h' for more information.
+	EndUsage
+    exit 1
+}
+
+shorthelp()
+{
+    cat <<-EndHelp
+		  Usage: $oneline_usage
+
+		  Actions:
+		    add|a "THING I NEED TO DO +project @context"
+		    addm "THINGS I NEED TO DO
+		          MORE THINGS I NEED TO DO"
+		    addto DEST "TEXT TO ADD"
+		    append|app ITEM# "TEXT TO APPEND"
+		    archive
+		    command [ACTIONS]
+		    deduplicate
+		    del|rm ITEM# [TERM]
+		    depri|dp ITEM#[, ITEM#, ITEM#, ...]
+		    do ITEM#[, ITEM#, ITEM#, ...]
+		    help
+		    list|ls [TERM...]
+		    listall|lsa [TERM...]
+		    listaddons
+		    listcon|lsc
+		    listfile|lf [SRC [TERM...]]
+		    listpri|lsp [PRIORITIES] [TERM...]
+		    listproj|lsprj [TERM...]
+		    move|mv ITEM# DEST [SRC]
+		    prepend|prep ITEM# "TEXT TO PREPEND"
+		    pri|p ITEM# PRIORITY
+		    replace ITEM# "UPDATED TODO"
+		    report
+		    shorthelp
+
+		  Actions can be added and overridden using scripts in the actions
+		  directory.
+	EndHelp
+
+    # Only list the one-line usage from the add-on actions. This assumes that
+    # add-ons use the same usage indentation structure as todo.sh.
+    addonHelp | grep -e '^  Add-on Actions:' -e '^    [[:alpha:]]'
+
+    cat <<-EndHelpFooter
+
+		  See "help" for more details.
+	EndHelpFooter
+    exit 0
+}
+
+help()
+{
+    cat <<-EndOptionsHelp
+		  Usage: $oneline_usage
+
+		  Options:
+		    -@
+		        Hide context names in list output.  Use twice to show context
+		        names (default).
+		    -+
+		        Hide project names in list output.  Use twice to show project
+		        names (default).
+		    -c
+		        Color mode
+		    -d CONFIG_FILE
+		        Use a configuration file other than the default ~/.todo/config
+		    -f
+		        Forces actions without confirmation or interactive input
+		    -h
+		        Display a short help message; same as action "shorthelp"
+		    -p
+		        Plain mode turns off colors
+		    -P
+		        Hide priority labels in list output.  Use twice to show
+		        priority labels (default).
+		    -a
+		        Don't auto-archive tasks automatically on completion
+		    -A
+		        Auto-archive tasks automatically on completion
+		    -n
+		        Don't preserve line numbers; automatically remove blank lines
+		        on task deletion
+		    -N
+		        Preserve line numbers
+		    -t
+		        Prepend the current date to a task automatically
+		        when it's added.
+		    -T
+		        Do not prepend the current date to a task automatically
+		        when it's added.
+		    -v
+		        Verbose mode turns on confirmation messages
+		    -vv
+		        Extra verbose mode prints some debugging information and
+		        additional help text
+		    -V
+		        Displays version, license and credits
+		    -x
+		        Disables TODOTXT_FINAL_FILTER
+
+
+	EndOptionsHelp
+
+    [ $TODOTXT_VERBOSE -gt 1 ] && cat <<-'EndVerboseHelp'
+		  Environment variables:
+		    TODOTXT_AUTO_ARCHIVE            is same as option -a (0)/-A (1)
+		    TODOTXT_CFG_FILE=CONFIG_FILE    is same as option -d CONFIG_FILE
+		    TODOTXT_FORCE=1                 is same as option -f
+		    TODOTXT_PRESERVE_LINE_NUMBERS   is same as option -n (0)/-N (1)
+		    TODOTXT_PLAIN                   is same as option -p (1)/-c (0)
+		    TODOTXT_DATE_ON_ADD             is same as option -t (1)/-T (0)
+		    TODOTXT_VERBOSE=1               is same as option -v
+		    TODOTXT_DISABLE_FILTER=1        is same as option -x
+		    TODOTXT_DEFAULT_ACTION=""       run this when called with no arguments
+		    TODOTXT_SORT_COMMAND="sort ..." customize list output
+		    TODOTXT_FINAL_FILTER="sed ..."  customize list after color, P@+ hiding
+		    TODOTXT_SOURCEVAR=\$DONE_FILE   use another source for listcon, listproj
+
+
+	EndVerboseHelp
+    cat <<-EndActionsHelp
+		  Built-in Actions:
+		    add "THING I NEED TO DO +project @context"
+		    a "THING I NEED TO DO +project @context"
+		      Adds THING I NEED TO DO to your todo.txt file on its own line.
+		      Project and context notation optional.
+		      Quotes optional.
+
+		    addm "FIRST THING I NEED TO DO +project1 @context
+		    SECOND THING I NEED TO DO +project2 @context"
+		      Adds FIRST THING I NEED TO DO to your todo.txt on its own line and
+		      Adds SECOND THING I NEED TO DO to you todo.txt on its own line.
+		      Project and context notation optional.
+
+		    addto DEST "TEXT TO ADD"
+		      Adds a line of text to any file located in the todo.txt directory.
+		      For example, addto inbox.txt "decide about vacation"
+
+		    append ITEM# "TEXT TO APPEND"
+		    app ITEM# "TEXT TO APPEND"
+		      Adds TEXT TO APPEND to the end of the task on line ITEM#.
+		      Quotes optional.
+
+		    archive
+		      Moves all done tasks from todo.txt to done.txt and removes blank lines.
+
+		    command [ACTIONS]
+		      Runs the remaining arguments using only todo.sh builtins.
+		      Will not call any .todo.actions.d scripts.
+
+		    deduplicate
+		      Removes duplicate lines from todo.txt.
+
+		    del ITEM# [TERM]
+		    rm ITEM# [TERM]
+		      Deletes the task on line ITEM# in todo.txt.
+		      If TERM specified, deletes only TERM from the task.
+
+		    depri ITEM#[, ITEM#, ITEM#, ...]
+		    dp ITEM#[, ITEM#, ITEM#, ...]
+		      Deprioritizes (removes the priority) from the task(s)
+		      on line ITEM# in todo.txt.
+
+		    do ITEM#[, ITEM#, ITEM#, ...]
+		      Marks task(s) on line ITEM# as done in todo.txt.
+
+		    help
+		      Display this help message.
+
+		    list [TERM...]
+		    ls [TERM...]
+		      Displays all tasks that contain TERM(s) sorted by priority with line
+		      numbers.  Each task must match all TERM(s) (logical AND); to display
+		      tasks that contain any TERM (logical OR), use
+		      "TERM1\|TERM2\|..." (with quotes), or TERM1\\\|TERM2 (unquoted).
+		      Hides all tasks that contain TERM(s) preceded by a
+		      minus sign (i.e. -TERM). If no TERM specified, lists entire todo.txt.
+
+		    listall [TERM...]
+		    lsa [TERM...]
+		      Displays all the lines in todo.txt AND done.txt that contain TERM(s)
+		      sorted by priority with line  numbers.  Hides all tasks that
+		      contain TERM(s) preceded by a minus sign (i.e. -TERM).  If no
+		      TERM specified, lists entire todo.txt AND done.txt
+		      concatenated and sorted.
+
+		    listaddons
+		      Lists all added and overridden actions in the actions directory.
+
+		    listcon
+		    lsc
+		      Lists all the task contexts that start with the @ sign in todo.txt.
+
+		    listfile [SRC [TERM...]]
+		    lf [SRC [TERM...]]
+		      Displays all the lines in SRC file located in the todo.txt directory,
+		      sorted by priority with line  numbers.  If TERM specified, lists
+		      all lines that contain TERM(s) in SRC file.  Hides all tasks that
+		      contain TERM(s) preceded by a minus sign (i.e. -TERM).  
+		      Without any arguments, the names of all text files in the todo.txt
+		      directory are listed.
+		
+		    listpri [PRIORITIES] [TERM...]
+		    lsp [PRIORITIES] [TERM...]
+		      Displays all tasks prioritized PRIORITIES.
+		      PRIORITIES can be a single one (A) or a range (A-C).
+		      If no PRIORITIES specified, lists all prioritized tasks.
+		      If TERM specified, lists only prioritized tasks that contain TERM(s).
+		      Hides all tasks that contain TERM(s) preceded by a minus sign
+		      (i.e. -TERM).  
+
+		    listproj
+		    lsprj
+		      Lists all the projects (terms that start with a + sign) in
+		      todo.txt.
+
+		    move ITEM# DEST [SRC]
+		    mv ITEM# DEST [SRC]
+		      Moves a line from source text file (SRC) to destination text file (DEST).
+		      Both source and destination file must be located in the directory defined
+		      in the configuration directory.  When SRC is not defined
+		      it's by default todo.txt.
+
+		    prepend ITEM# "TEXT TO PREPEND"
+		    prep ITEM# "TEXT TO PREPEND"
+		      Adds TEXT TO PREPEND to the beginning of the task on line ITEM#.
+		      Quotes optional.
+
+		    pri ITEM# PRIORITY
+		    p ITEM# PRIORITY
+		      Adds PRIORITY to task on line ITEM#.  If the task is already
+		      prioritized, replaces current priority with new PRIORITY.
+		      PRIORITY must be a letter between A and Z.
+
+		    replace ITEM# "UPDATED TODO"
+		      Replaces task on line ITEM# with UPDATED TODO.
+
+		    report
+		      Adds the number of open tasks and done tasks to report.txt.
+
+		    shorthelp
+		      List the one-line usage of all built-in and add-on actions.
+
+	EndActionsHelp
+
+        addonHelp
+    exit 1
+}
+
+addonHelp()
+{
+    if [ -d "$TODO_ACTIONS_DIR" ]; then
+        didPrintAddonActionsHeader=
+        for action in "$TODO_ACTIONS_DIR"/*
+        do
+            if [ -f "$action" -a -x "$action" ]; then
+                if [ ! "$didPrintAddonActionsHeader" ]; then
+                    cat <<-EndAddonActionsHeader
+		  Add-on Actions:
+	EndAddonActionsHeader
+                    didPrintAddonActionsHeader=1
+                fi
+                "$action" usage
+            fi
+        done
+    fi
+}
+
+die()
+{
+    echo "$*"
+    exit 1
+}
+
+cleaninput()
+{
+    # Parameters:    When $1 = "for sed", performs additional escaping for use
+    #                in sed substitution with "|" separators.
+    # Precondition:  $input contains text to be cleaned.
+    # Postcondition: Modifies $input.
+
+    # Replace CR and LF with space; tasks always comprise a single line.
+    input=${input//$'\r'/ }
+    input=${input//$'\n'/ }
+
+    if [ "$1" = "for sed" ]; then
+        # This action uses sed with "|" as the substitution separator, and & as
+        # the matched string; these must be escaped.
+        # Backslashes must be escaped, too, and before the other stuff.
+        input=${input//\\/\\\\}
+        input=${input//|/\\|}
+        input=${input//&/\\&}
+    fi
+}
+
+getPrefix()
+{
+    # Parameters:    $1: todo file; empty means $TODO_FILE.
+    # Returns:       Uppercase FILE prefix to be used in place of "TODO:" where
+    #                a different todo file can be specified.
+    local base=$(basename "${1:-$TODO_FILE}")
+    echo "${base%%.[^.]*}" | tr 'a-z' 'A-Z'
+}
+
+getTodo()
+{
+    # Parameters:    $1: task number
+    #                $2: Optional todo file
+    # Precondition:  $errmsg contains usage message.
+    # Postcondition: $todo contains task text.
+
+    local item=$1
+    [ -z "$item" ] && die "$errmsg"
+    [ "${item//[0-9]/}" ] && die "$errmsg"
+
+    todo=$(sed "$item!d" "${2:-$TODO_FILE}")
+    [ -z "$todo" ] && die "$(getPrefix "$2"): No task $item."
+}
+getNewtodo()
+{
+    # Parameters:    $1: task number
+    #                $2: Optional todo file
+    # Precondition:  None.
+    # Postcondition: $newtodo contains task text.
+
+    local item=$1
+    [ -z "$item" ] && die 'Programming error: $item should exist.'
+    [ "${item//[0-9]/}" ] && die 'Programming error: $item should be numeric.'
+
+    newtodo=$(sed "$item!d" "${2:-$TODO_FILE}")
+    [ -z "$newtodo" ] && die "$(getPrefix "$2"): No updated task $item."
+}
+
+replaceOrPrepend()
+{
+  action=$1; shift
+  case "$action" in
+    replace)
+      backref=
+      querytext="Replacement: "
+      ;;
+    prepend)
+      backref=' &'
+      querytext="Prepend: "
+      ;;
+  esac
+  shift; item=$1; shift
+  getTodo "$item"
+
+  if [[ -z "$1" && $TODOTXT_FORCE = 0 ]]; then
+    echo -n "$querytext"
+    read input
+  else
+    input=$*
+  fi
+  cleaninput "for sed"
+
+  # Retrieve existing priority and prepended date
+  priority=$(sed -e "$item!d" -e $item's/^\((.) \)\{0,1\}\([0-9]\{2,4\}-[0-9]\{2\}-[0-9]\{2\} \)\{0,1\}.*/\1/' "$TODO_FILE")
+  prepdate=$(sed -e "$item!d" -e $item's/^\((.) \)\{0,1\}\([0-9]\{2,4\}-[0-9]\{2\}-[0-9]\{2\} \)\{0,1\}.*/\2/' "$TODO_FILE")
+
+  if [ "$prepdate" -a "$action" = "replace" ] && [ "$(echo "$input"|sed -e 's/^\([0-9]\{2,4\}-[0-9]\{2\}-[0-9]\{2\}\)\{0,1\}.*/\1/')" ]; then
+    # If the replaced text starts with a date, it will replace the existing
+    # date, too.
+    prepdate=
+  fi
+
+  # Temporarily remove any existing priority and prepended date, perform the
+  # change (replace/prepend) and re-insert the existing priority and prepended
+  # date again.
+  sed -i.bak -e "$item s/^${priority}${prepdate}//" -e "$item s|^.*|${priority}${prepdate}${input}${backref}|" "$TODO_FILE"
+  if [ $TODOTXT_VERBOSE -gt 0 ]; then
+    getNewtodo "$item"
+    case "$action" in
+      replace)
+        echo "$item $todo"
+        echo "TODO: Replaced task with:"
+        echo "$item $newtodo"
+        ;;
+      prepend)
+        echo "$item $newtodo"
+        ;;
+    esac
+  fi
+}
+
+#Preserving environment variables so they don't get clobbered by the config file
+OVR_TODOTXT_AUTO_ARCHIVE="$TODOTXT_AUTO_ARCHIVE"
+OVR_TODOTXT_FORCE="$TODOTXT_FORCE"
+OVR_TODOTXT_PRESERVE_LINE_NUMBERS="$TODOTXT_PRESERVE_LINE_NUMBERS"
+OVR_TODOTXT_PLAIN="$TODOTXT_PLAIN"
+OVR_TODOTXT_DATE_ON_ADD="$TODOTXT_DATE_ON_ADD"
+OVR_TODOTXT_DISABLE_FILTER="$TODOTXT_DISABLE_FILTER"
+OVR_TODOTXT_VERBOSE="$TODOTXT_VERBOSE"
+OVR_TODOTXT_DEFAULT_ACTION="$TODOTXT_DEFAULT_ACTION"
+OVR_TODOTXT_SORT_COMMAND="$TODOTXT_SORT_COMMAND"
+OVR_TODOTXT_FINAL_FILTER="$TODOTXT_FINAL_FILTER"
+
+# == PROCESS OPTIONS ==
+while getopts ":fhpcnNaAtTvVx+@Pd:" Option
+do
+  case $Option in
+    '@' )
+        ## HIDE_CONTEXT_NAMES starts at zero (false); increment it to one
+        ##   (true) the first time this flag is seen. Each time the flag
+        ##   is seen after that, increment it again so that an even
+        ##   number shows context names and an odd number hides context
+        ##   names.
+        : $(( HIDE_CONTEXT_NAMES++ ))
+        if [ $(( $HIDE_CONTEXT_NAMES % 2 )) -eq 0 ]
+        then
+            ## Zero or even value -- show context names
+            unset HIDE_CONTEXTS_SUBSTITUTION
+        else
+            ## One or odd value -- hide context names
+            export HIDE_CONTEXTS_SUBSTITUTION='[[:space:]]@[[:graph:]]\{1,\}'
+        fi
+        ;;
+    '+' )
+        ## HIDE_PROJECT_NAMES starts at zero (false); increment it to one
+        ##   (true) the first time this flag is seen. Each time the flag
+        ##   is seen after that, increment it again so that an even
+        ##   number shows project names and an odd number hides project
+        ##   names.
+        : $(( HIDE_PROJECT_NAMES++ ))
+        if [ $(( $HIDE_PROJECT_NAMES % 2 )) -eq 0 ]
+        then
+            ## Zero or even value -- show project names
+            unset HIDE_PROJECTS_SUBSTITUTION
+        else
+            ## One or odd value -- hide project names
+            export HIDE_PROJECTS_SUBSTITUTION='[[:space:]][+][[:graph:]]\{1,\}'
+        fi
+        ;;
+    a )
+        OVR_TODOTXT_AUTO_ARCHIVE=0
+        ;;
+    A )
+        OVR_TODOTXT_AUTO_ARCHIVE=1
+        ;;
+    c )
+        OVR_TODOTXT_PLAIN=0
+        ;;
+    d )
+        TODOTXT_CFG_FILE=$OPTARG
+        ;;
+    f )
+        OVR_TODOTXT_FORCE=1
+        ;;
+    h )
+        # Short-circuit option parsing and forward to the action.
+        # Cannot just invoke shorthelp() because we need the configuration
+        # processed to locate the add-on actions directory.
+        set -- '-h' 'shorthelp'
+        ;;
+    n )
+        OVR_TODOTXT_PRESERVE_LINE_NUMBERS=0
+        ;;
+    N )
+        OVR_TODOTXT_PRESERVE_LINE_NUMBERS=1
+        ;;
+    p )
+        OVR_TODOTXT_PLAIN=1
+        ;;
+    P )
+        ## HIDE_PRIORITY_LABELS starts at zero (false); increment it to one
+        ##   (true) the first time this flag is seen. Each time the flag
+        ##   is seen after that, increment it again so that an even
+        ##   number shows priority labels and an odd number hides priority
+        ##   labels.
+        : $(( HIDE_PRIORITY_LABELS++ ))
+        if [ $(( $HIDE_PRIORITY_LABELS % 2 )) -eq 0 ]
+        then
+            ## Zero or even value -- show priority labels
+            unset HIDE_PRIORITY_SUBSTITUTION
+        else
+            ## One or odd value -- hide priority labels
+            export HIDE_PRIORITY_SUBSTITUTION="([A-Z])[[:space:]]"
+        fi
+        ;;
+    t )
+        OVR_TODOTXT_DATE_ON_ADD=1
+        ;;
+    T )
+        OVR_TODOTXT_DATE_ON_ADD=0
+        ;;
+    v )
+        : $(( TODOTXT_VERBOSE++ ))
+        ;;
+    V )
+        version
+        ;;
+    x )
+        OVR_TODOTXT_DISABLE_FILTER=1
+        ;;
+  esac
+done
+shift $(($OPTIND - 1))
+
+# defaults if not yet defined
+TODOTXT_VERBOSE=${TODOTXT_VERBOSE:-1}
+TODOTXT_PLAIN=${TODOTXT_PLAIN:-0}
+TODOTXT_CFG_FILE=${TODOTXT_CFG_FILE:-$HOME/.todo/config}
+TODOTXT_FORCE=${TODOTXT_FORCE:-0}
+TODOTXT_PRESERVE_LINE_NUMBERS=${TODOTXT_PRESERVE_LINE_NUMBERS:-1}
+TODOTXT_AUTO_ARCHIVE=${TODOTXT_AUTO_ARCHIVE:-1}
+TODOTXT_DATE_ON_ADD=${TODOTXT_DATE_ON_ADD:-0}
+TODOTXT_DEFAULT_ACTION=${TODOTXT_DEFAULT_ACTION:-}
+TODOTXT_SORT_COMMAND=${TODOTXT_SORT_COMMAND:-env LC_COLLATE=C sort -f -k2}
+TODOTXT_DISABLE_FILTER=${TODOTXT_DISABLE_FILTER:-}
+TODOTXT_FINAL_FILTER=${TODOTXT_FINAL_FILTER:-cat}
+
+# Export all TODOTXT_* variables
+export ${!TODOTXT_@}
+
+# Default color map
+export NONE=''
+export BLACK='\\033[0;30m'
+export RED='\\033[0;31m'
+export GREEN='\\033[0;32m'
+export BROWN='\\033[0;33m'
+export BLUE='\\033[0;34m'
+export PURPLE='\\033[0;35m'
+export CYAN='\\033[0;36m'
+export LIGHT_GREY='\\033[0;37m'
+export DARK_GREY='\\033[1;30m'
+export LIGHT_RED='\\033[1;31m'
+export LIGHT_GREEN='\\033[1;32m'
+export YELLOW='\\033[1;33m'
+export LIGHT_BLUE='\\033[1;34m'
+export LIGHT_PURPLE='\\033[1;35m'
+export LIGHT_CYAN='\\033[1;36m'
+export WHITE='\\033[1;37m'
+export DEFAULT='\\033[0m'
+
+# Default priority->color map.
+export PRI_A=$YELLOW        # color for A priority
+export PRI_B=$GREEN         # color for B priority
+export PRI_C=$LIGHT_BLUE    # color for C priority
+export PRI_X=$WHITE         # color unless explicitly defined
+
+# Default highlight colors.
+export COLOR_DONE=$LIGHT_GREY   # color for done (but not yet archived) tasks
+
+# Default sentence delimiters for todo.sh append.
+# If the text to be appended to the task begins with one of these characters, no
+# whitespace is inserted in between. This makes appending to an enumeration
+# (todo.sh add 42 ", foo") syntactically correct.
+export SENTENCE_DELIMITERS=',.:;'
+
+[ -e "$TODOTXT_CFG_FILE" ] || {
+    CFG_FILE_ALT="$HOME/todo.cfg"
+
+    if [ -e "$CFG_FILE_ALT" ]
+    then
+        TODOTXT_CFG_FILE="$CFG_FILE_ALT"
+    fi
+}
+
+[ -e "$TODOTXT_CFG_FILE" ] || {
+    CFG_FILE_ALT="$HOME/.todo.cfg"
+
+    if [ -e "$CFG_FILE_ALT" ]
+    then
+        TODOTXT_CFG_FILE="$CFG_FILE_ALT"
+    fi
+}
+
+[ -e "$TODOTXT_CFG_FILE" ] || {
+    CFG_FILE_ALT=$(dirname "$0")"/todo.cfg"
+
+    if [ -e "$CFG_FILE_ALT" ]
+    then
+        TODOTXT_CFG_FILE="$CFG_FILE_ALT"
+    fi
+}
+
+
+if [ -z "$TODO_ACTIONS_DIR" -o ! -d "$TODO_ACTIONS_DIR" ]
+then
+    TODO_ACTIONS_DIR="$HOME/.todo/actions"
+    export TODO_ACTIONS_DIR
+fi
+
+[ -d "$TODO_ACTIONS_DIR" ] || {
+    TODO_ACTIONS_DIR_ALT="$HOME/.todo.actions.d"
+
+    if [ -d "$TODO_ACTIONS_DIR_ALT" ]
+    then
+        TODO_ACTIONS_DIR="$TODO_ACTIONS_DIR_ALT"
+    fi
+}
+
+# === SANITY CHECKS (thanks Karl!) ===
+[ -r "$TODOTXT_CFG_FILE" ] || die "Fatal Error: Cannot read configuration file $TODOTXT_CFG_FILE"
+
+. "$TODOTXT_CFG_FILE"
+
+# === APPLY OVERRIDES
+if [ -n "$OVR_TODOTXT_AUTO_ARCHIVE" ] ; then
+  TODOTXT_AUTO_ARCHIVE="$OVR_TODOTXT_AUTO_ARCHIVE"
+fi
+if [ -n "$OVR_TODOTXT_FORCE" ] ; then
+  TODOTXT_FORCE="$OVR_TODOTXT_FORCE"
+fi
+if [ -n "$OVR_TODOTXT_PRESERVE_LINE_NUMBERS" ] ; then
+  TODOTXT_PRESERVE_LINE_NUMBERS="$OVR_TODOTXT_PRESERVE_LINE_NUMBERS"
+fi
+if [ -n "$OVR_TODOTXT_PLAIN" ] ; then
+  TODOTXT_PLAIN="$OVR_TODOTXT_PLAIN"
+fi
+if [ -n "$OVR_TODOTXT_DATE_ON_ADD" ] ; then
+  TODOTXT_DATE_ON_ADD="$OVR_TODOTXT_DATE_ON_ADD"
+fi
+if [ -n "$OVR_TODOTXT_DISABLE_FILTER" ] ; then
+  TODOTXT_DISABLE_FILTER="$OVR_TODOTXT_DISABLE_FILTER"
+fi
+if [ -n "$OVR_TODOTXT_VERBOSE" ] ; then
+  TODOTXT_VERBOSE="$OVR_TODOTXT_VERBOSE"
+fi
+if [ -n "$OVR_TODOTXT_DEFAULT_ACTION" ] ; then
+  TODOTXT_DEFAULT_ACTION="$OVR_TODOTXT_DEFAULT_ACTION"
+fi
+if [ -n "$OVR_TODOTXT_SORT_COMMAND" ] ; then
+  TODOTXT_SORT_COMMAND="$OVR_TODOTXT_SORT_COMMAND"
+fi
+if [ -n "$OVR_TODOTXT_FINAL_FILTER" ] ; then
+  TODOTXT_FINAL_FILTER="$OVR_TODOTXT_FINAL_FILTER"
+fi
+
+ACTION=${1:-$TODOTXT_DEFAULT_ACTION}
+
+[ -z "$ACTION" ]    && usage
+[ -d "$TODO_DIR" ]  || die "Fatal Error: $TODO_DIR is not a directory"
+( cd "$TODO_DIR" )  || die "Fatal Error: Unable to cd to $TODO_DIR"
+
+[ -f "$TODO_FILE" ] || cp /dev/null "$TODO_FILE"
+[ -f "$DONE_FILE" ] || cp /dev/null "$DONE_FILE"
+[ -f "$REPORT_FILE" ] || cp /dev/null "$REPORT_FILE"
+
+if [ $TODOTXT_PLAIN = 1 ]; then
+    for clr in ${!PRI_@}; do
+        export $clr=$NONE
+    done
+    PRI_X=$NONE
+    DEFAULT=$NONE
+    COLOR_DONE=$NONE
+fi
+
+_addto() {
+    file="$1"
+    input="$2"
+    cleaninput
+
+    if [[ $TODOTXT_DATE_ON_ADD = 1 ]]; then
+        now=$(date '+%Y-%m-%d')
+        input=$(echo "$input" | sed -e 's/^\(([A-Z]) \)\{0,1\}/\1'"$now /")
+    fi
+    echo "$input" >> "$file"
+    if [ $TODOTXT_VERBOSE -gt 0 ]; then
+        TASKNUM=$(sed -n '$ =' "$file")
+        echo "$TASKNUM $input"
+        echo "$(getPrefix "$file"): $TASKNUM added."
+    fi
+}
+
+shellquote()
+{
+    typeset -r qq=\'; printf %s\\n "'${1//\'/${qq}\\${qq}${qq}}'";
+}
+
+filtercommand()
+{
+    filter=${1:-}
+    shift
+    post_filter=${1:-}
+    shift
+
+    for search_term
+    do
+        ## See if the first character of $search_term is a dash
+        if [ "${search_term:0:1}" != '-' ]
+        then
+            ## First character isn't a dash: hide lines that don't match
+            ## this $search_term
+            filter="${filter:-}${filter:+ | }grep -i $(shellquote "$search_term")"
+        else
+            ## First character is a dash: hide lines that match this
+            ## $search_term
+            #
+            ## Remove the first character (-) before adding to our filter command
+            filter="${filter:-}${filter:+ | }grep -v -i $(shellquote "${search_term:1}")"
+        fi
+    done
+
+    [ -n "$post_filter" ] && {
+        filter="${filter:-}${filter:+ | }${post_filter:-}"
+    }
+
+    printf %s "$filter"
+}
+
+_list() {
+    local FILE="$1"
+    ## If the file starts with a "/" use absolute path. Otherwise,
+    ## try to find it in either $TODO_DIR or using a relative path
+    if [ "${1:0:1}" == / ]; then
+        ## Absolute path
+        src="$FILE"
+    elif [ -f "$TODO_DIR/$FILE" ]; then
+        ## Path relative to todo.sh directory
+        src="$TODO_DIR/$FILE"
+    elif [ -f "$FILE" ]; then
+        ## Path relative to current working directory
+        src="$FILE"
+    elif [ -f "$TODO_DIR/${FILE}.txt" ]; then
+        ## Path relative to todo.sh directory, missing file extension
+        src="$TODO_DIR/${FILE}.txt"
+    else
+        die "TODO: File $FILE does not exist."
+    fi
+
+    ## Get our search arguments, if any
+    shift ## was file name, new $1 is first search term
+
+    _format "$src" '' "$@"
+
+    if [ $TODOTXT_VERBOSE -gt 0 ]; then
+        echo "--"
+        echo "$(getPrefix "$src"): ${NUMTASKS:-0} of ${TOTALTASKS:-0} tasks shown"
+    fi
+}
+getPadding()
+{
+    ## We need one level of padding for each power of 10 $LINES uses.
+    LINES=$(sed -n '$ =' "${1:-$TODO_FILE}")
+    printf %s ${#LINES}
+}
+_format()
+{
+    # Parameters:    $1: todo input file; when empty formats stdin
+    #                $2: ITEM# number width; if empty auto-detects from $1 / $TODO_FILE.
+    # Precondition:  None
+    # Postcondition: $NUMTASKS and $TOTALTASKS contain statistics (unless $TODOTXT_VERBOSE=0).
+
+    FILE=$1
+    shift
+
+    ## Figure out how much padding we need to use, unless this was passed to us.
+    PADDING=${1:-$(getPadding "$FILE")}
+    shift
+
+    ## Number the file, then run the filter command,
+    ## then sort and mangle output some more
+    if [[ $TODOTXT_DISABLE_FILTER = 1 ]]; then
+        TODOTXT_FINAL_FILTER="cat"
+    fi
+    items=$(
+        if [ "$FILE" ]; then
+            sed = "$FILE"
+        else
+            sed =
+        fi                                                      \
+        | sed -e '''
+            N
+            s/^/     /
+            s/ *\([ 0-9]\{'"$PADDING"',\}\)\n/\1 /
+            /^[ 0-9]\{1,\} *$/d
+         '''
+    )
+
+    ## Build and apply the filter.
+    filter_command=$(filtercommand "${pre_filter_command:-}" "${post_filter_command:-}" "$@")
+    if [ "${filter_command}" ]; then
+        filtered_items=$(echo -n "$items" | eval "${filter_command}")
+    else
+        filtered_items=$items
+    fi
+    filtered_items=$(
+        echo -n "$filtered_items"                              \
+        | sed '''
+            s/^     /00000/;
+            s/^    /0000/;
+            s/^   /000/;
+            s/^  /00/;
+            s/^ /0/;
+          ''' \
+        | eval ${TODOTXT_SORT_COMMAND}                                        \
+        | awk '''
+            function highlight(colorVar,      color) {
+                color = ENVIRON[colorVar]
+                gsub(/\\+033/, "\033", color)
+                return color
+            }
+            {
+                if (match($0, /^[0-9]+ x /)) {
+                    print highlight("COLOR_DONE") $0 highlight("DEFAULT")
+                } else if (match($0, /^[0-9]+ \([A-Z]\) /)) {
+                    clr = highlight("PRI_" substr($0, RSTART + RLENGTH - 3, 1))
+                    print \
+                        (clr ? clr : highlight("PRI_X")) \
+                        (ENVIRON["HIDE_PRIORITY_SUBSTITUTION"] == "" ? $0 : substr($0, 1, RLENGTH - 4) substr($0, RSTART + RLENGTH)) \
+                        highlight("DEFAULT")
+                } else { print }
+            }
+          '''  \
+        | sed '''
+            s/'"${HIDE_PROJECTS_SUBSTITUTION:-^}"'//g
+            s/'"${HIDE_CONTEXTS_SUBSTITUTION:-^}"'//g
+            s/'"${HIDE_CUSTOM_SUBSTITUTION:-^}"'//g
+          '''                                                   \
+        | eval ${TODOTXT_FINAL_FILTER}                          \
+    )
+    [ "$filtered_items" ] && echo "$filtered_items"
+
+    if [ $TODOTXT_VERBOSE -gt 0 ]; then
+        NUMTASKS=$( echo -n "$filtered_items" | sed -n '$ =' )
+        TOTALTASKS=$( echo -n "$items" | sed -n '$ =' )
+    fi
+    if [ $TODOTXT_VERBOSE -gt 1 ]; then
+        echo "TODO DEBUG: Filter Command was: ${filter_command:-cat}"
+    fi
+}
+
+export -f cleaninput getPrefix getTodo getNewtodo shellquote filtercommand _list getPadding _format die
+
+# == HANDLE ACTION ==
+action=$( printf "%s\n" "$ACTION" | tr 'A-Z' 'a-z' )
+
+## If the first argument is "command", run the rest of the arguments
+## using todo.sh builtins.
+## Else, run a actions script with the name of the command if it exists
+## or fallback to using a builtin
+if [ "$action" == command ]
+then
+    ## Get rid of "command" from arguments list
+    shift
+    ## Reset action to new first argument
+    action=$( printf "%s\n" "$1" | tr 'A-Z' 'a-z' )
+elif [ -d "$TODO_ACTIONS_DIR" -a -x "$TODO_ACTIONS_DIR/$action" ]
+then
+    "$TODO_ACTIONS_DIR/$action" "$@"
+    exit $?
+fi
+
+## Only run if $action isn't found in .todo.actions.d
+case $action in
+"add" | "a")
+    if [[ -z "$2" && $TODOTXT_FORCE = 0 ]]; then
+        echo -n "Add: "
+        read input
+    else
+        [ -z "$2" ] && die "usage: $TODO_SH add \"TODO ITEM\""
+        shift
+        input=$*
+    fi
+    _addto "$TODO_FILE" "$input"
+    ;;
+
+"addm")
+    if [[ -z "$2" && $TODOTXT_FORCE = 0 ]]; then
+        echo -n "Add: "
+        read input
+    else
+        [ -z "$2" ] && die "usage: $TODO_SH addm \"TODO ITEM\""
+        shift
+        input=$*
+    fi
+
+    # Set Internal Field Seperator as newline so we can 
+    # loop across multiple lines
+    SAVEIFS=$IFS
+    IFS=$'\n'
+
+    # Treat each line seperately
+    for line in $input ; do
+        _addto "$TODO_FILE" "$line"
+    done
+    IFS=$SAVEIFS
+    ;;
+
+"addto" )
+    [ -z "$2" ] && die "usage: $TODO_SH addto DEST \"TODO ITEM\""
+    dest="$TODO_DIR/$2"
+    [ -z "$3" ] && die "usage: $TODO_SH addto DEST \"TODO ITEM\""
+    shift
+    shift
+    input=$*
+
+    if [ -f "$dest" ]; then
+        _addto "$dest" "$input"
+    else
+        die "TODO: Destination file $dest does not exist."
+    fi
+    ;;
+
+"append" | "app" )
+    errmsg="usage: $TODO_SH append ITEM# \"TEXT TO APPEND\""
+    shift; item=$1; shift
+    getTodo "$item"
+
+    if [[ -z "$1" && $TODOTXT_FORCE = 0 ]]; then
+        echo -n "Append: "
+        read input
+    else
+        input=$*
+    fi
+    case "$input" in
+      [$SENTENCE_DELIMITERS]*)  appendspace=;;
+      *)                        appendspace=" ";;
+    esac
+    cleaninput "for sed"
+
+    if sed -i.bak $item" s|^.*|&${appendspace}${input}|" "$TODO_FILE"; then
+        if [ $TODOTXT_VERBOSE -gt 0 ]; then
+            getNewtodo "$item"
+            echo "$item $newtodo"
+	fi
+    else
+        die "TODO: Error appending task $item."
+    fi
+    ;;
+
+"archive" )
+    # defragment blank lines
+    sed -i.bak -e '/./!d' "$TODO_FILE"
+    [ $TODOTXT_VERBOSE -gt 0 ] && grep "^x " "$TODO_FILE"
+    grep "^x " "$TODO_FILE" >> "$DONE_FILE"
+    sed -i.bak '/^x /d' "$TODO_FILE"
+    if [ $TODOTXT_VERBOSE -gt 0 ]; then
+	echo "TODO: $TODO_FILE archived."
+    fi
+    ;;
+
+"del" | "rm" )
+    # replace deleted line with a blank line when TODOTXT_PRESERVE_LINE_NUMBERS is 1
+    errmsg="usage: $TODO_SH del ITEM# [TERM]"
+    item=$2
+    getTodo "$item"
+
+    if [ -z "$3" ]; then
+        if  [ $TODOTXT_FORCE = 0 ]; then
+            echo "Delete '$todo'?  (y/n)"
+            read ANSWER
+        else
+            ANSWER="y"
+        fi
+        if [ "$ANSWER" = "y" ]; then
+            if [ $TODOTXT_PRESERVE_LINE_NUMBERS = 0 ]; then
+                # delete line (changes line numbers)
+                sed -i.bak -e $item"s/^.*//" -e '/./!d' "$TODO_FILE"
+            else
+                # leave blank line behind (preserves line numbers)
+                sed -i.bak -e $item"s/^.*//" "$TODO_FILE"
+            fi
+            if [ $TODOTXT_VERBOSE -gt 0 ]; then
+                echo "$item $todo"
+                echo "TODO: $item deleted."
+            fi
+        else
+            echo "TODO: No tasks were deleted."
+        fi
+    else
+        sed -i.bak \
+            -e $item"s/^\((.) \)\{0,1\} *$3 */\1/g" \
+            -e $item"s/ *$3 *\$//g" \
+            -e $item"s/  *$3 */ /g" \
+            -e $item"s/ *$3  */ /g" \
+            -e $item"s/$3//g" \
+            "$TODO_FILE"
+        getNewtodo "$item"
+        if [ "$todo" = "$newtodo" ]; then
+            [ $TODOTXT_VERBOSE -gt 0 ] && echo "$item $todo"
+            die "TODO: '$3' not found; no removal done."
+        fi
+        if [ $TODOTXT_VERBOSE -gt 0 ]; then
+            echo "$item $todo"
+            echo "TODO: Removed '$3' from task."
+            echo "$item $newtodo"
+        fi
+    fi
+    ;;
+
+"depri" | "dp" )
+    errmsg="usage: $TODO_SH depri ITEM#[, ITEM#, ITEM#, ...]"
+    shift;
+    [ $# -eq 0 ] && die "$errmsg"
+
+    # Split multiple depri's, if comma separated change to whitespace separated
+    # Loop the 'depri' function for each item
+    for item in ${*//,/ }; do
+        getTodo "$item"
+
+	if [[ "$todo" = \(?\)\ * ]]; then
+	    sed -i.bak -e $item"s/^(.) //" "$TODO_FILE"
+	    if [ $TODOTXT_VERBOSE -gt 0 ]; then
+		getNewtodo "$item"
+		echo "$item $newtodo"
+		echo "TODO: $item deprioritized."
+	    fi
+	else
+	    echo "TODO: $item is not prioritized."
+	fi
+    done
+    ;;
+
+"do" )
+    errmsg="usage: $TODO_SH do ITEM#[, ITEM#, ITEM#, ...]"
+    # shift so we get arguments to the do request
+    shift;
+    [ "$#" -eq 0 ] && die "$errmsg"
+
+    # Split multiple do's, if comma separated change to whitespace separated
+    # Loop the 'do' function for each item
+    for item in ${*//,/ }; do
+        getTodo "$item"
+
+        # Check if this item has already been done
+        if [ "${todo:0:2}" != "x " ]; then
+            now=$(date '+%Y-%m-%d')
+            # remove priority once item is done
+            sed -i.bak $item"s/^(.) //" "$TODO_FILE"
+            sed -i.bak $item"s|^|x $now |" "$TODO_FILE"
+            if [ $TODOTXT_VERBOSE -gt 0 ]; then
+                getNewtodo "$item"
+                echo "$item $newtodo"
+                echo "TODO: $item marked as done."
+	    fi
+        else
+            echo "TODO: $item is already marked done."
+        fi
+    done
+
+    if [ $TODOTXT_AUTO_ARCHIVE = 1 ]; then
+        # Recursively invoke the script to allow overriding of the archive
+        # action.
+        "$TODO_FULL_SH" archive
+    fi
+    ;;
+
+"help" )
+    if [ -t 1 ] ; then # STDOUT is a TTY
+        if which "${PAGER:-less}" >/dev/null 2>&1; then
+            # we have a working PAGER (or less as a default)
+            help | "${PAGER:-less}" && exit 0
+        fi
+    fi
+    help # just in case something failed above, we go ahead and just spew to STDOUT
+    ;;
+
+"shorthelp" )
+    if [ -t 1 ] ; then # STDOUT is a TTY
+        if which "${PAGER:-less}" >/dev/null 2>&1; then
+            # we have a working PAGER (or less as a default)
+            shorthelp | "${PAGER:-less}" && exit 0
+        fi
+    fi
+    shorthelp # just in case something failed above, we go ahead and just spew to STDOUT
+    ;;
+
+"list" | "ls" )
+    shift  ## Was ls; new $1 is first search term
+    _list "$TODO_FILE" "$@"
+    ;;
+
+"listall" | "lsa" )
+    shift  ## Was lsa; new $1 is first search term
+
+    TOTAL=$( sed -n '$ =' "$TODO_FILE" )
+    PADDING=${#TOTAL}
+
+    post_filter_command="awk -v TOTAL=$TOTAL -v PADDING=$PADDING '{ \$1 = sprintf(\"%\" PADDING \"d\", (\$1 > TOTAL ? 0 : \$1)); print }' "
+    cat "$TODO_FILE" "$DONE_FILE" | TODOTXT_VERBOSE=0 _format '' "$PADDING" "$@"
+
+    if [ $TODOTXT_VERBOSE -gt 0 ]; then
+        TDONE=$( sed -n '$ =' "$DONE_FILE" )
+        TASKNUM=$(TODOTXT_PLAIN=1 TODOTXT_VERBOSE=0 _format "$TODO_FILE" 1 "$@" | sed -n '$ =')
+        DONENUM=$(TODOTXT_PLAIN=1 TODOTXT_VERBOSE=0 _format "$DONE_FILE" 1 "$@" | sed -n '$ =')
+        echo "--"
+        echo "$(getPrefix "$TODO_FILE"): ${TASKNUM:-0} of ${TOTAL:-0} tasks shown"
+        echo "$(getPrefix "$DONE_FILE"): ${DONENUM:-0} of ${TDONE:-0} tasks shown"
+        echo "total $((TASKNUM + DONENUM)) of $((TOTAL + TDONE)) tasks shown"
+    fi
+    ;;
+
+"listfile" | "lf" )
+    shift  ## Was listfile, next $1 is file name
+    if [ $# -eq 0 ]; then
+        [ $TODOTXT_VERBOSE -gt 0 ] && echo "Files in the todo.txt directory:"
+        cd "$TODO_DIR" && ls -1 *.txt
+    else
+        FILE="$1"
+        shift  ## Was filename; next $1 is first search term
+
+        _list "$FILE" "$@"
+    fi
+    ;;
+
+"listcon" | "lsc" )
+    FILE=$TODO_FILE
+    [ "$TODOTXT_SOURCEVAR" ] && eval "FILE=$TODOTXT_SOURCEVAR"
+    grep -ho '[^ ]*@[^ ]\+' "${FILE[@]}" | grep '^@' | sort -u
+    ;;
+
+"listproj" | "lsprj" )
+    FILE=$TODO_FILE
+    [ "$TODOTXT_SOURCEVAR" ] && eval "FILE=$TODOTXT_SOURCEVAR"
+    shift
+    eval "$(filtercommand 'cat "${FILE[@]}"' '' "$@")" | grep -o '[^ ]*+[^ ]\+' | grep '^+' | sort -u
+    ;;
+
+"listpri" | "lsp" )
+    shift ## was "listpri", new $1 is priority to list or first TERM
+
+    pri=$(printf "%s\n" "$1" | tr 'a-z' 'A-Z' | grep -e '^[A-Z]$' -e '^[A-Z]-[A-Z]$') && shift || pri="A-Z"
+    post_filter_command="grep '^ *[0-9]\+ ([${pri}]) '"
+    _list "$TODO_FILE" "$@"
+    ;;
+
+"move" | "mv" )
+    # replace moved line with a blank line when TODOTXT_PRESERVE_LINE_NUMBERS is 1
+    errmsg="usage: $TODO_SH mv ITEM# DEST [SRC]"
+    item=$2
+    dest="$TODO_DIR/$3"
+    src="$TODO_DIR/$4"
+
+    [ -z "$4" ] && src="$TODO_FILE"
+    [ -z "$dest" ] && die "$errmsg"
+
+    [ -f "$src" ] || die "TODO: Source file $src does not exist."
+    [ -f "$dest" ] || die "TODO: Destination file $dest does not exist."
+
+    getTodo "$item" "$src"
+    [ -z "$todo" ] && die "$item: No such item in $src."
+    if  [ $TODOTXT_FORCE = 0 ]; then
+        echo "Move '$todo' from $src to $dest? (y/n)"
+        read ANSWER
+    else
+        ANSWER="y"
+    fi
+    if [ "$ANSWER" = "y" ]; then
+        if [ $TODOTXT_PRESERVE_LINE_NUMBERS = 0 ]; then
+            # delete line (changes line numbers)
+            sed -i.bak -e $item"s/^.*//" -e '/./!d' "$src"
+        else
+            # leave blank line behind (preserves line numbers)
+            sed -i.bak -e $item"s/^.*//" "$src"
+        fi
+        echo "$todo" >> "$dest"
+
+        if [ $TODOTXT_VERBOSE -gt 0 ]; then
+            echo "$item $todo"
+            echo "TODO: $item moved from '$src' to '$dest'."
+        fi
+    else
+        echo "TODO: No tasks moved."
+    fi
+    ;;
+
+"prepend" | "prep" )
+    errmsg="usage: $TODO_SH prepend ITEM# \"TEXT TO PREPEND\""
+    replaceOrPrepend 'prepend' "$@"
+    ;;
+
+"pri" | "p" )
+    item=$2
+    newpri=$( printf "%s\n" "$3" | tr 'a-z' 'A-Z' )
+
+    errmsg="usage: $TODO_SH pri ITEM# PRIORITY
+note: PRIORITY must be anywhere from A to Z."
+
+    [ "$#" -ne 3 ] && die "$errmsg"
+    [[ "$newpri" = @([A-Z]) ]] || die "$errmsg"
+    getTodo "$item"
+
+    oldpri=
+    if [[ "$todo" = \(?\)\ * ]]; then
+        oldpri=${todo:1:1}
+    fi
+
+    if [ "$oldpri" != "$newpri" ]; then
+        sed -i.bak -e $item"s/^(.) //" -e $item"s/^/($newpri) /" "$TODO_FILE"
+    fi
+    if [ $TODOTXT_VERBOSE -gt 0 ]; then
+        getNewtodo "$item"
+        echo "$item $newtodo"
+        if [ "$oldpri" != "$newpri" ]; then
+            if [ "$oldpri" ]; then
+                echo "TODO: $item re-prioritized from ($oldpri) to ($newpri)."
+            else
+                echo "TODO: $item prioritized ($newpri)."
+            fi
+        fi
+    fi
+    if [ "$oldpri" = "$newpri" ]; then
+        echo "TODO: $item already prioritized ($newpri)."
+    fi
+    ;;
+
+"replace" )
+    errmsg="usage: $TODO_SH replace ITEM# \"UPDATED ITEM\""
+    replaceOrPrepend 'replace' "$@"
+    ;;
+
+"report" )
+    # archive first
+    # Recursively invoke the script to allow overriding of the archive
+    # action.
+    "$TODO_FULL_SH" archive
+
+    TOTAL=$( sed -n '$ =' "$TODO_FILE" )
+    TDONE=$( sed -n '$ =' "$DONE_FILE" )
+    NEWDATA="${TOTAL:-0} ${TDONE:-0}"
+    LASTREPORT=$(sed -ne '$p' "$REPORT_FILE")
+    LASTDATA=${LASTREPORT#* }   # Strip timestamp.
+    if [ "$LASTDATA" = "$NEWDATA" ]; then
+        echo "$LASTREPORT"
+        [ $TODOTXT_VERBOSE -gt 0 ] && echo "TODO: Report file is up-to-date."
+    else
+        NEWREPORT="$(date +%Y-%m-%dT%T) ${NEWDATA}"
+        echo "${NEWREPORT}" >> "$REPORT_FILE"
+        echo "${NEWREPORT}"
+        [ $TODOTXT_VERBOSE -gt 0 ] && echo "TODO: Report file updated."
+    fi
+    ;;
+
+"deduplicate" )
+    if [ $TODOTXT_PRESERVE_LINE_NUMBERS = 0 ]; then
+        deduplicateSedCommand='d'
+    else
+        deduplicateSedCommand='s/^.*//; p'
+    fi
+
+    # To determine the difference when deduplicated lines are preserved, only
+    # non-empty lines must be counted.
+    originalTaskNum=$( sed -e '/./!d' "$TODO_FILE" | sed -n '$ =' )
+
+    # Look for duplicate lines and discard the second occurrence.
+    # We start with an empty hold space on the first line.  For each line:
+    #   G - appends newline + hold space to the pattern space
+    #   s/\n/&&/; - double up the first new line so we catch adjacent dups
+    #   /^\([^\n]*\n\).*\n\1/b dedup
+    #       If the first line of the hold space shows up again later as an
+    #       entire line, it's a duplicate. Jump to the "dedup" label, where
+    #       either of the following is executed, depending on whether empty
+    #       lines should be preserved:
+    #       d           - Delete the current pattern space, quit this line and
+    #                     move on to the next, or:
+    #       s/^.*//; p  - Clear the task text, print this line and move on to
+    #                     the next.
+    #   s/\n//;   - else (no duplicate), drop the doubled newline
+    #   h;        - replace the hold space with the expanded pattern space
+    #   P;        - print up to the first newline (that is, the input line)
+    #   b         - end processing of the current line
+    sed -i.bak -n \
+        -e 'G; s/\n/&&/; /^\([^\n]*\n\).*\n\1/b dedup' \
+        -e 's/\n//; h; P; b' \
+        -e ':dedup' \
+        -e "$deduplicateSedCommand" \
+        "$TODO_FILE"
+
+    newTaskNum=$( sed -e '/./!d' "$TODO_FILE" | sed -n '$ =' )
+    deduplicateNum=$(( originalTaskNum - newTaskNum ))
+    if [ $deduplicateNum -eq 0 ]; then
+        echo "TODO: No duplicate tasks found"
+    else
+        echo "TODO: $deduplicateNum duplicate task(s) removed"
+    fi
+    ;;
+
+"listaddons" )
+    if [ -d "$TODO_ACTIONS_DIR" ]; then
+        cd "$TODO_ACTIONS_DIR" || exit $?
+        for action in *
+        do
+            if [ -f "$action" -a -x "$action" ]; then
+                echo "$action"
+            fi
+        done
+    fi
+    ;;
+
+* )
+    usage;;
+esac
diff --git a/plugins/available/todo/todo_completion b/plugins/available/todo/todo_completion
new file mode 100644
index 0000000..3f9d308
--- /dev/null
+++ b/plugins/available/todo/todo_completion
@@ -0,0 +1,107 @@
+#!/bin/bash source-this-script
+[ "$BASH_VERSION" ] || return
+
+_todo()
+{
+    local cur prev opts
+    COMPREPLY=()
+    cur="${COMP_WORDS[COMP_CWORD]}"
+    prev="${COMP_WORDS[COMP_CWORD-1]}"
+
+    local -r OPTS="-@ -@@ -+ -++ -d -f -h -p -P -PP -a -n -t -v -vv -V -x"
+    local -r COMMANDS="\
+        add a addto addm append app archive command del  \
+        rm depri dp do help list ls listaddons listall lsa listcon  \
+        lsc listfile lf listpri lsp listproj lsprj move \
+        mv prepend prep pri p replace report shorthelp"
+
+    local _todo_sh=${_todo_sh:-todo.sh}
+    local completions
+    if [ $COMP_CWORD -eq 1 ]; then
+        completions="$COMMANDS $(eval TODOTXT_VERBOSE=0 $_todo_sh command listaddons) $OPTS"
+    elif [[ $COMP_CWORD -gt 2 && ( \
+        "${COMP_WORDS[COMP_CWORD-2]}" =~ ^(move|mv)$ || \
+        "${COMP_WORDS[COMP_CWORD-3]}" =~ ^(move|mv)$ ) ]]; then
+        # "move ITEM# DEST [SRC]" has file arguments on positions 2 and 3.
+        completions=$(eval TODOTXT_VERBOSE=0 $_todo_sh command listfile)
+    else
+        case "$prev" in
+            command)
+                completions=$COMMANDS;;
+            addto|listfile|lf)
+                completions=$(eval TODOTXT_VERBOSE=0 $_todo_sh command listfile);;
+            -*) completions="$COMMANDS $(eval TODOTXT_VERBOSE=0 $_todo_sh command listaddons) $OPTS";;
+            *)  case "$cur" in
+                    +*) completions=$(eval TODOTXT_VERBOSE=0 $_todo_sh command listproj)
+                        COMPREPLY=( $( compgen -W "$completions" -- $cur ))
+                        [ ${#COMPREPLY[@]} -gt 0 ] && return 0
+                        # Fall back to projects extracted from done tasks.
+                        completions=$(eval 'TODOTXT_VERBOSE=0 TODOTXT_SOURCEVAR=\$DONE_FILE' $_todo_sh command listproj)
+                        ;;
+                    @*) completions=$(eval TODOTXT_VERBOSE=0 $_todo_sh command listcon)
+                        COMPREPLY=( $( compgen -W "$completions" -- $cur ))
+                        [ ${#COMPREPLY[@]} -gt 0 ] && return 0
+                        # Fall back to contexts extracted from done tasks.
+                        completions=$(eval 'TODOTXT_VERBOSE=0 TODOTXT_SOURCEVAR=\$DONE_FILE' $_todo_sh command listcon)
+                        ;;
+                    *)  if [[ "$cur" =~ ^[0-9]+$ ]]; then
+                            # Remove the (padded) task number; we prepend the
+                            # user-provided $cur instead.
+                            # Remove the timestamp prepended by the -t option,
+                            # and the done date (for done tasks); there's no
+                            # todo.txt option for that yet.
+                            # But keep priority and "x"; they're short and may
+                            # provide useful context.
+                            # Remove any trailing whitespace; the Bash
+                            # completion inserts a trailing space itself.
+                            # Finally, limit the output to a single line just as
+                            # a safety check of the ls action output.
+                            local todo=$( \
+                                eval TODOTXT_VERBOSE=0 $_todo_sh '-@ -+ -p -x command ls "^ *${cur} "' | \
+                                sed -e 's/^ *[0-9]\{1,\} //' -e 's/\((.) \)[0-9]\{2,4\}-[0-9]\{2\}-[0-9]\{2\} /\1/' \
+                                    -e 's/\([xX] \)\([0-9]\{2,4\}-[0-9]\{2\}-[0-9]\{2\} \)\{1,2\}/\1/' \
+                                    -e 's/[[:space:]]*$//' \
+                                    -e '1q' \
+                            )
+                            # Append task text as a shell comment. This
+                            # completion can be a safety check before a
+                            # destructive todo.txt operation.
+                            [ "$todo" ] && COMPREPLY[0]="$cur # $todo"
+                            return 0
+                        else
+                            return 0
+                        fi
+                        ;;
+                esac
+                ;;
+        esac
+    fi
+
+    COMPREPLY=( $( compgen -W "$completions" -- $cur ))
+    return 0
+}
+complete -F _todo todo.sh
+
+# If you define an alias (e.g. "t") to todo.sh, you need to explicitly enable
+# completion for it, too:
+#complete -F _todo t
+
+# If you have renamed the todo.sh executable, or if it is not accessible through
+# PATH, you need to add and use a wrapper completion function, like this:
+#_todoElsewhere()
+#{
+#    local _todo_sh='/path/to/todo2.sh'
+#    _todo "$@"
+#}
+#complete -F _todoElsewhere /path/to/todo2.sh
+
+# If you use aliases to use different configuration(s), you need to add and use
+# a wrapper completion function for each configuration if you want to complete
+# fron the actual configured task locations:
+#alias todo2='todo.sh -d "$HOME/todo2.cfg"'
+#_todo2()
+#{
+#    local _todo_sh='todo.sh -d "$HOME/todo2.cfg"'
+#    _todo "$@"
+#}
+#complete -F _todo2 todo2
diff --git a/plugins/available/vagrant.plugins.bash b/plugins/available/vagrant.plugin.bash
similarity index 94%
rename from plugins/available/vagrant.plugins.bash
rename to plugins/available/vagrant.plugin.bash
index 3131744..aa223e7 100644
--- a/plugins/available/vagrant.plugins.bash
+++ b/plugins/available/vagrant.plugin.bash
@@ -1,4 +1,6 @@
-#!/usr/bin/env bash
+cite about-plugin
+about-plugin 'set up vagrant autocompletion'
+
 _vagrant()
 {
     cur="${COMP_WORDS[COMP_CWORD]}"
diff --git a/plugins/available/virtualenv.plugin.bash b/plugins/available/virtualenv.plugin.bash
index ba27c76..302e19d 100644
--- a/plugins/available/virtualenv.plugin.bash
+++ b/plugins/available/virtualenv.plugin.bash
@@ -1,19 +1,30 @@
-#!/usr/bin/env bash
-
 # make sure virtualenvwrapper is enabled if available
+
+cite about-plugin
+about-plugin 'virtualenvwrapper helper functions'
+
 [[ `which virtualenvwrapper.sh` ]] && . virtualenvwrapper.sh
 
-# create a new virtualenv for this directory
+
 function mkvenv {
+  about 'create a new virtualenv for this directory'
+  group 'virtualenv'
+
   cwd=`basename \`pwd\``
   mkvirtualenv --no-site-packages --distribute $cwd
 }
 
-# create a new virtualenv for the branch you're currently in
+
 function mkvbranch {
+  about 'create a new virtualenv for the current branch'
+  group 'virtualenv'
+
   mkvirtualenv --no-site-packages --distribute "$(basename `pwd`)@$(git_prompt_info)"
 }
 
 function wovbranch {
+  about 'sets workon branch'
+  group 'virtualenv'
+
   workon "$(basename `pwd`)@$(git_prompt_info)"
 }
diff --git a/plugins/available/xterm.plugins.bash b/plugins/available/xterm.plugins.bash
deleted file mode 100644
index 3e4ac9e..0000000
--- a/plugins/available/xterm.plugins.bash
+++ /dev/null
@@ -1,17 +0,0 @@
-set_xterm_title () {
-    local title="$1"
-    echo -ne "\e]0;$title\007"
-}
-
-
-precmd () {
-    set_xterm_title "${USER}@${HOSTNAME} `dirs -0` $PROMPTCHAR"
-}
-
-preexec () {
-    set_xterm_title "$1 {`dirs -0`} (${USER}@${HOSTNAME})"
-}
-
-case "$TERM" in
-    xterm*|rxvt*) preexec_install;;
-esac
diff --git a/plugins/available/z.bash b/plugins/available/z.plugin.bash
similarity index 91%
rename from plugins/available/z.bash
rename to plugins/available/z.plugin.bash
index ef3e3d9..6653575 100644
--- a/plugins/available/z.bash
+++ b/plugins/available/z.plugin.bash
@@ -1,7 +1,7 @@
-#!/usr/bin/env bash
+cite about-plugin
+about-plugin 'maintains a jump-list of the directories you actually use'
+about-plugin '                              z is DEPRECATED, use fasd instead'
 
-# maintains a jump-list of the directories you actually use
-#
 # INSTALL:
 #   * put something like this in your .bashrc:
 #     . /path/to/z.sh
@@ -15,6 +15,11 @@
 #   * z -t foo  # goes to most recently accessed dir matching foo
 #   * z -l foo  # list all dirs matching foo (by frecency)
 
+if [ -e $BASH_IT/plugins/enabled/fasd.plugin.bash ]; then
+    printf '%s\n' 'sorry, the z plugin is incompatible with the fasd plugin. you may use either, but not both.'
+    return
+fi
+
 z() {
  local datafile="$HOME/.z"
  if [ "$1" = "--add" ]; then
diff --git a/plugins/available/z_autoenv.plugins.bash b/plugins/available/z_autoenv.plugin.bash
similarity index 87%
rename from plugins/available/z_autoenv.plugins.bash
rename to plugins/available/z_autoenv.plugin.bash
index 04efa85..dd1aec1 100644
--- a/plugins/available/z_autoenv.plugins.bash
+++ b/plugins/available/z_autoenv.plugin.bash
@@ -1,4 +1,6 @@
-#!/usr/bin/env bash
+cite about-plugin
+about-plugin 'source into environment when cding to directories'
+
 if [[ -n "${ZSH_VERSION}" ]]
 then __array_offset=0
 else __array_offset=1
diff --git a/themes/base.theme.bash b/themes/base.theme.bash
index fe112ec..f096fda 100644
--- a/themes/base.theme.bash
+++ b/themes/base.theme.bash
@@ -31,7 +31,7 @@
 RBFU_THEME_PROMPT_SUFFIX='|'
 
 function scm {
-  if [[ -d .git ]]; then SCM=$SCM_GIT
+  if [[ -f .git/HEAD ]]; then SCM=$SCM_GIT
   elif [[ -n "$(git symbolic-ref HEAD 2> /dev/null)" ]]; then SCM=$SCM_GIT
   elif [[ -d .hg ]]; then SCM=$SCM_HG
   elif [[ -n "$(hg root 2> /dev/null)" ]]; then SCM=$SCM_HG
@@ -121,7 +121,7 @@
 
 function rbenv_version_prompt {
   if which rbenv &> /dev/null; then
-    rbenv=$(rbenv global) || return
+    rbenv=$(rbenv version-name) || return
     echo -e "$RBENV_THEME_PROMPT_PREFIX$rbenv$RBENV_THEME_PROMPT_SUFFIX"
   fi
 }
diff --git a/themes/candy/candy.theme.bash b/themes/candy/candy.theme.bash
index e568964..77f8ff4 100644
--- a/themes/candy/candy.theme.bash
+++ b/themes/candy/candy.theme.bash
@@ -1,6 +1,6 @@
 #!/usr/bin/env bash
 function prompt_command() {
-    PS1="${green}\u@\h ${blue}\T ${reset_color}${white}\w${reset_color}$(scm_prompt_info)\]${blue} →${bold_blue} ${reset_color} ";
+    PS1="${green}\u@\h ${blue}\T ${reset_color}${white}\w${reset_color}$(scm_prompt_info)${blue} →${bold_blue} ${reset_color} ";
 }
 
 PROMPT_COMMAND=prompt_command;
diff --git a/themes/doubletime/doubletime.theme.bash b/themes/doubletime/doubletime.theme.bash
index 6756976..87ced7e 100644
--- a/themes/doubletime/doubletime.theme.bash
+++ b/themes/doubletime/doubletime.theme.bash
@@ -59,14 +59,15 @@
 PROMPT_COMMAND=prompt_setter
 
 git_prompt_status() {
-
-  if [ -n "$(git status | grep 'Changes not staged' 2> /dev/null)" ]; then
+  local git_status_output
+  git_status_output=$(git status 2> /dev/null )
+  if [ -n "$(echo $git_status_output | grep 'Changes not staged')" ]; then
     git_status="${bold_red}$(scm_prompt_info) ✗"
-  elif [ -n "$(git status | grep 'Changes to be committed' 2> /dev/null)" ]; then
+  elif [ -n "$(echo $git_status_output | grep 'Changes to be committed')" ]; then
      git_status="${bold_yellow}$(scm_prompt_info) ^"
-  elif [ -n "$(git status | grep 'Untracked files' 2> /dev/null)" ]; then
+  elif [ -n "$(echo $git_status_output | grep 'Untracked files')" ]; then
      git_status="${bold_cyan}$(scm_prompt_info) +"
-  elif [ -n "$(git status | grep 'nothing to commit' 2> /dev/null)" ]; then
+  elif [ -n "$(echo $git_status_output | grep 'nothing to commit')" ]; then
      git_status="${bold_green}$(scm_prompt_info) ${green}✓"
   else
     git_status="$(scm_prompt_info)"
@@ -74,18 +75,3 @@
   echo "[$git_status${normal}]"
 
 }
-
-# git_prompt_color() {
-#
-#   if [ -n "$(git status | grep 'Changes not staged' 2> /dev/null)" ]; then
-#     git_status='${bold_red} ✗'
-#   elif [ -n "$(git status | grep 'Changes to be committed' 2> /dev/null)" ]; then
-#      git_status='${bold_yellow} ^'
-#   elif [ -n "$(git status | grep 'Untracked files' 2> /dev/null)" ]; then
-#      git_status='${bold_cyan} +'
-#   else
-#     git_status='${bold_green} ✓'
-#   fi
-#   echo $git_status
-#
-# }