10. Shell Integration

Since you usually run Git commands on the shell, you should add functionality to them to interact with Git. Especially for Git beginners, such interaction between shell and Git is very helpful to keep track of things.

There are two areas in which the shell can help you:

  • Display important information about a repository at the prompt.

  • This way you don’t have to call git status and consorts too often.

  • A custom completion helps you to enter git commands correctly, even if you don’t know the exact syntax.

A good prompt should signal the state of the working tree in addition to the current branch. Are there any changes that are not yet saved? Are there already changes in the index?

A good completion should, for example, when entering git checkout and then pressing the Tab key, only offer branches from the repository for completion. But if you type git checkout -- only files should be completed. This saves time and protects against typos. Other completions are also useful, such as the existing remotes for git push and git pull.

In this chapter we will introduce basic recipes for two popular shells: the Bash and the Z-Shell. Instructions for other interactive shells can be found on the Internet.

The topic of shell integration is very extensive, so the tutorials presented here are only guidelines and ideas and do not claim to be complete. To make matters worse, the git community is developing the user interface - i.e. the existing subcommands and their options - very quickly. So please don’t be surprised if the completion is “lagging behind” and brand new subcommands and options are not (yet) available.

10.1. Git and the Bash

Both the functionality for completion and the status commands for the prompt are implemented in a script called git-completion.bash. It is managed together with the sources for Git. You can find the file in the contrib/completion directory of the Git project. Often the completion is already provided by your distribution or the git installer for your operating system. If you have installed the git package in Debian or Ubuntu, the file should already be in /usr/share/bash-completion/completions/git. In Gentoo, you install the file via the USE flag bash-completion of dev-vcs/git. The current maintainer is Shawn O. Pearce.

10.1.1. Completions

To activate the completion, load the script with the command source and pass the corresponding file as argument, e.g:

source ~/Downloads/git-2.1.0/contrib/completion/git-completion.bash

The completion completes among other things:

Git subcommands

For example, if you type git pu[TAB], the bash will offer you pull and push:

$ git pu[TAB]
pull push

Note: Only the porcelain commands and user aliases are available. External and plumbing commands are not implemented. Subcommands that have additional subcommands themselves, e.g. git remote or git stash, are also completed:

$ git remote [TAB]
add     prune     rename     rm     set-head     show     update
Local Branches and Tags

Useful for subcommands, such as checkout and rebase, that expect a local reference:

$ git branch
* master
  refactor-cmd-line
  refactor-profiling
$ git checkout refactor-[TAB]
refactor-cmd-line    refactor-profiling
Configured Remotes

Commands like git fetch and git remote are often called with a remote as argument. Completion helps here too:

$ git remote show [TAB]
github        sourceforge
Remote Branches and Tags

The Completion can also “check” on the remote page to see which references are available. This is done for example with the command git pull, which expects a remote reference or a refspec:

$ git pull origin v1.7.1[TAB]
v1.7.1       v1.7.1.2     v1.7.1.4     v1.7.1-rc1
v1.7.1.1     v1.7.1.3     v1.7.1-rc0   v1.7.1-rc2

Of course this only works if the remote repository is available. In most cases a network connection and at least read access is required.

Options

Most subcommands have several long options like --bare. The completion usually knows these and completes them accordingly:

$ git diff --color[TAB]
--color         --color-words

Short options, such as -a, are not completed.

Files

For Git commands that expect file names. Good examples are git add and git checkout:

$ git add [TAB]
.git/     hello.py  README    test/
$ git checkout -- [TAB]
.git/     hello.py  README    test/
Git configuration options

The bash completion for Git also completes configuration options that you set with git config:

$ git config user.[TAB]
user.email        user.name         user.signingkey

As usual with bash completion, the input is automatically completed when it is unique. If only the Branch feature exists, typing git checkout fe[TAB] will cause fe to be completed; the command git checkout feature will then appear on the command line - press Enter to execute the command. Only when the input is ambiguous does the bash display the possible completions.

10.1.2. The Prompt

Beside the completion there is another script to display information about the git repository in the prompt. For this you have to load the file contrib/completion/git-prompt.sh (maybe it is also installed by your distribution, e.g. under /usr/lib/git-core/git-sh-prompt). Then, as in the following example, place a call to the __git_ps1 function in the PS1 variable. The function takes a so-called _format string expression_ as argument - i.e. the string %s is replaced by git infos, all other characters are taken over.

source /usr/lib/git-core/git-sh-prompt
PS1=_\u@\h \w$(__git_ps1 " (%s)") $ _

The characters are replaced as follows: \u is the username, \h is the hostname, \w is the current working directory and $(__git_ps1 " (%s)") are the git infos, which without additional configuration (see below) consist only of the branch name:

esc@creche \~ $ cd git-working/git
esc@creche ~/git-working/git (master) $

The format string expression allows you to customize the display of the git info by using additional characters or color codes, e.g. with the following prompt:

PS1=_\u@\h \w$(__git_ps1 " (git)-[%s]") $ _

This looks like this:

esc@creche ~/git-working/git (git)-[master] $

If the current commit is not referenced by a branch (Detached-HEAD), either the tag or the abbreviated SHA-1 sum is displayed, each surrounded by a pair of brackets:

esc@creche ~/git-working/git (git)-[(v1.7.1.4)] $
esc@creche ~/git-working/git (git)-[(e760924...)] $

If you are inside the $GIT_DIR or in a bare repository, this is signaled accordingly:

esc@creche ~/git-working/git/.git (git)-[GIT_DIR!] $
esc@creche ~/git-working/git.git/.git (git)-[BARE:master] $

It also indicates when you are in the middle of a merge, rebase or similar state where only certain operations are possible:

esc@creche ~/git-working/git (git)-[master|REBASE-i] $

You can also expand the display to show the status of the Working Trees using different icons. To do this, you must set the following environment variables to a non-empty value, e.g. to 1.

GIT_PS1_SHOWDIRTYSTATE

For changes that are not yet in the index (unstaged), an asterisk (*) is displayed. For changes that are already in the index (staged), a plus (+) is displayed. The display requires the working tree to be read - this may slow down the shell for large repositories (Git has to check every file for modifications). You can therefore disable this behavior for individual repositories with the Git variable bash.showDirtyState:

$ git config bash.showDirtyState false
GIT_PS1_SHOWSTASHSTATE

If you have created one or more stashes, this is indicated by the dollar sign ($) in the prompt.

GIT_PS1_SHOWUNTRACKEDFILES

The existence of (untracked files) is indicated by a percentage sign (%).

You can activate all this additional information as follows:

GIT_PS1_SHOWDIRTYSTATE=1
GIT_PS1_SHOWSTASHSTATE=1
GIT_PS1_SHOWUNTRACKEDFILES=1

If everything in the repository matches (i.e. unstaged, staged, stashed and untracked), four additional characters (*, +, $ and %) are displayed in the prompt:

esc@creche ~/git-working/git (git)-[master *+$%] $

In newer Git versions, the script has a new feature that shows the relationship to the upstream branch (@{upstream}). Enable this feature by setting GIT_PS1_SHOWUPSTREAM to the value git.⁠[134] The prompt then signals all states described in Section 5.5.2, "Comparison with the Upstream": up-to-date with the equal sign (=); ahead with the greater-than sign (>); behind with the less-than sign (<); diverged with both a greater-than sign and a less-than sign (><). For example:

esc@creche ~/git-working/git (git)-[master >] $

This function is implemented with the --count option of the git rev-list plumbing command, which does not exist in old git versions, like 1.7.1. If you have such an old git version, but a current script and want to use this display anyway, set the value of the environment variable to legacy - the script will then use an alternative implementation that works without the said option. If you also want to know how far ahead or behind the branch is, add the value verbose. The prompt will also show the number of different commits:

esc@creche ~/git-working/git (git)-[master u+2] $

The desired values are to be assigned to the environment variable as a list:

GIT_PS1_SHOWUPSTREAM="legacy verbose git"

10.2. Git and the Z-Shell

Both completion and prompt functions are always included with the Z-Shell.

The Z-Shell has a very useful feature to call man pages: the run-help function. It is called by default with Esc+H in Emacs mode and displays the man page for the command that is already on the command line:

$ man[ESC]+[h]
#Man-Page man(1) is displayed

However, since Git consists of subcommands and each subcommand has its own man page, run-help does not work very well - only the man page git(1) is displayed. The included run-help-git function can help here:

$ git rebase[ESC][h]
#Man-Page git(1) is displayed
$ unalias run-help
$ autoload run-help
$ autoload run-help-git
$ git rebase[ESC][h]
#Man-Page git-rebase(1) is displayed

10.2.1. Completions

To activate completion for Git, first load the completion system:

$ autoload -Uz compinit && compinit

The completion completes among other things:

Git subcommands

Subcommands are also completed in the Z-Shell. The difference to Bash is that the Z-Shell displays a short description in addition to the actual command:

$ git pu[TAB]
pull     -- fetch from and merge with a remote repository
push     -- update remote refs along with associated objects

The same applies to subcommands, which themselves have subcommands:

$ git remote [TAB]
add      -- add a new remote
prune    -- delete all stale tracking branches for a given remote
rename   -- rename a remote from .git/config and update all...
rm       -- remove a remote from .git/config and all...
show     -- show information about a given remote
update   -- fetch updates for a set of remotes

As well as user aliases:

$ git t[TAB]
tag           -- create tag object signed with GPG
tree          -- alias for _log --oneline --graph --decorate -23_
Local Branches and Tags

The Z-Shell also completes local branches and tags - no difference to Bash.

Configured Remotes

Configured remotes are known to the Z-Shell. For subcommands where only a configured remote is possible, e.g. git remote show, only configured remotes are displayed. If this is not clear, e.g. git pull, additional mechanisms of the Z-Shell take effect and usually a long list is displayed, which consists of the entries in the files .ssh/config (the configured SSH hosts) and .ssh/known_hosts (hosts you have already logged in to).

Options

Unlike Bash, Z-Shell knows both long and short options and shows them including a short description of the option. Here is an excerpt:

$ git branch -[TAB]
-a              -- list both remote-tracking branches and local branches
--contains      -- only list branches which contain the specified commit
--force     -f  -- force the creation of a new branch
Files

The Z-Shell is also able to complete file names - but it is a bit smarter than Bash. For example, for git add and git checkout, only files that actually have changes are offered - files that can either be added to the index or reset. Files that do not qualify are not offered either.

Git configuration options

Like Bash, the Z-Shell completion for Git completes all configuration options for Git. The difference is that it also includes a short description of the options:

$ git config user.[TAB]
email        -- email address used for commits
name         -- full name used for commits
signingkey   -- default GPG key to use when creating signed tags

A big difference with the Z-Shell is the way it is completed. The Z-Shell uses the so-called menu completion. This means that the Z-Shell offers you the next possible completion by pressing the Tab key again.⁠[135]

$ git pu[TAB]
pull  -- fetch from and merge with another repository or local branch
push  -- update remote refs along with associated objects
$ git pu[TAB]
$ git pull[TAB]
$ git push

The Z-Shell is not (yet) able to complete references on the remote side - but this is on the to-do list. But the Z-Shell is already able to complete files over an SSH connection. This is especially useful in connection with public key authentication and preconfigured SSH hosts. Assume you have configured the following host in .ssh/config:

Host example
    HostName git.example.com
    User max

On the server in your home directory your projects are located as bare repositories: project1.git and project2.git. You also generated an SSH key and stored it in the .ssh/authorized_keys file on the server. You can now use completion across the SSH connection.

$ git clone example:[TAB]
projekt1.git/ projekt2.git/

This is made possible by the completion functions of the Z-shell for ssh.

10.2.2. The Prompt

The Z-Shell contains functions to add git info to the prompt. The functionality is part of the extensive vcs_info system, which knows about a dozen other version control programs besides Git, including Subversion, CVS and Mercurial. Detailed documentation can be found in the zshcontrib(1) man page, in the “Gathering Information From Version Control Systems” section. Here we will only present the settings and customization options relevant to Git.

First, you need to load vcs_info and adjust the prompt to display Git info. It’s important that the Z-Shell option prompt_subst is set; it ensures that variables in the prompt are actually replaced, and you must call vcs_info in the precmd function. precmd is called just before the prompt is displayed. The call vcs_info in it makes sure that the git info is actually stored in the variable ${vcs_info_msg_0_}. Add the following lines to your .zshrc if they are not already included:

# load vcs_info
autoload -Uz vcs_info
# activate prompt_subst
setopt prompt_subst
# define precmd
precmd () { vcs_info }
# Set prompt
PS1=_%n@%m %~${vcs_info_msg_0_} $ _

The prompt is composed as follows: %n is the username, %m is the hostname, %~ is the current working directory, and the variable ${vcs_info_msg_0_} contains the git info. It is important that the prompt is specified with single quotes. This saves the string ${vcs_info_msg_0_} and not the value of the variable. Only when the prompt is displayed is the value of the variable - i.e. the git info - substituted.

The above setting for PS1 looks like this:

esc@creche ~/git-working/git (git)-[master]- $

Since vcs_info works with a lot of version control systems, it’s worth activating only those you actually use:⁠[136]

zstyle _:vcs_info:*_ enable git

To customize vcs_info, use a so-called zstyle, a hierarchical configuration mechanism of the z-shell described in the zshmodules(1) man page.

Special states like merge or rebase operations are signaled accordingly:

esc@creche ~/git-working/git (git)-[master|bisect]- $

Also in case of a Detached-HEAD either the tag or the abbreviated SHA-1 sum is displayed:

esc@creche ~/git-working/git (git)-[v1.7.1.4] $
esc@creche ~/git-working/git (git)-[e760924...] $

The Z-Shell, like the Bash, can display states of the working tree. Switch this on with the following line:

zstyle _:vcs_info:git*:*_ check-for-changes true

For example, vcs_info shows a U for changes that are not yet in the index (unstaged) and an S for changes that you have included in the index (staged):

esc@creche ~/git-working/git (git)-[master]US- $

A big advantage of vcs_info is that it can be adapted very easily. For example, if you do not like the letters U and S, you can replace them with other characters, e.g. * and +:

zstyle _:vcs_info:git*:*_ unstagedstr _*_
zstyle _:vcs_info:git*:*_ stagedstr _+_

Thus the Zsh prompt now looks more and more like the example from the section on bash:

esc@creche ~/git-working/git (git)-[master]*+- $

To display such not yet stored information vcs_info must always examine the working tree. Since this is known to cause problems with large repositories, you can exclude certain patterns:

zstyle _:vcs_info:*_ disable-patterns "/home/esc/git-working/linux-2.6(|/*)"

Maybe you want to change the order of the characters. In this case you need to adjust two format string expressions: formats and actionformats. The first is the default format, the second is the format when you are in the middle of a merge, rebase or similar process:

zstyle _:vcs_info:git*:*_ formats " (%s)-[%b%u%c]"
zstyle _:vcs_info:git*:*_ actionformats " (%s)-[%b|%a%u%c]"

A selection of the most important characters can be found in the following table. For a detailed list, please refer to the above mentioned man page.

%s

version management system, in our case always git

%b

Current branch, e.g. master

%a

Current process, e.g. merge or rebase-i (only for actionformats)

%u

Character to indicate changes that are not yet in the index, e.g. U

%c

Character to indicate changes that are already in the index, e.g. S

With the above setting the prompt will look like this:

esc@creche ~/git-working/git (git)-[master*+] $

Unfortunately vcs_info cannot signal the existence of unknown files and created stashes by default. But since Z-Shell version 4.3.11 the system supports so called hooks — extensions, which inject additional information into the prompt. We will now introduce two such hooks, which implement the two missing features mentioned above.

The hooks for vcs_info are written as shell functions. Note that the function name has the prefix +vi- to avoid possible collisions. For a hook to really work it has to change a value in the associative array hook_com. In both examples we change the value of the entry staged by appending additional characters to mark certain states. We use the percent sign (%) to indicate unknown files and the dollar sign ($) for created stashes. The percent sign must be specified twice to prevent the Z-Shell from mistakenly interpreting it as formatting. For the hooks we use various plumbing commands (see Sec. 8.3, “Writing Your Own Git Commands”).

+vi-untracked(){
    if [[ $(git rev-parse --is-inside-work-tree 2> /dev/null) == _true_ ]] && \
        [[ -n $(git ls-files --others --exclude-standard) ]] ; then
        hook_com[staged]+=_%%_
    fi
}
+vi-stashed(){
    if git rev-parse --verify refs/stash &> /dev/null ; then
        hook_com[staged]+=_$_
    fi
}

We activate the hooks so that they are evaluated when the git info is set (+set-message):

zstyle _:vcs_info:git*+set-message:*_ hooks stashed untracked

As in the bash example above, four additional characters (*, +, $ and %) may be displayed in the prompt (unstaged, staged, stashed and untracked):

esc@creche ~/git-working/git (git)-[master*+$%] $

With such hooks it is possible to extend the prompt as desired. For example, vcs_info does not show by default whether you are inside the $GIT_DIR or in a bare repository. With an appropriate hook you can include these signals in the prompt.

For more examples, see the Misc/vcs_info-examples file in the Z-Shell repository, including a hook that indicates the upstream branch relationship (section “Compare local changes to remote changes”). A minimal configuration for the Z-Shell according to the examples in this section can be found in the Scripts Collection for this book.⁠[137]


134. If you use git-svn, you can tell the script to use the SVN upstream (remotes/git-svn) for comparison (if it exists) instead of the upstream branch by setting the variable to the value auto.
135. The zshcompsys(1) man page describes how to further customize the completion. Especially the options group-name and menu-select are recommended.
136. A list of available systems can be obtained by calling the vcs_info_printsys function.
137. https://github.com/gitbuch/buch-scripte