Git offers so many commands and options that it’s virtually impossible to remember them all, especially when one doesn’t work with them daily. However, git also offers automatic completion that it perhaps not very knows but nevertheless very useful.

Let’s assume I want to see information about a particular remote repository. If I remember the command, I can type:

$ git remote show covid
* remote covid
  Fetch URL: https://github.com/CSSEGISandData/COVID-19.git
  Push  URL: https://github.com/CSSEGISandData/COVID-19.git
  HEAD branch: master
  Remote branches:
    CSSEGISandData-patch-02042020 tracked
    master                        tracked
    web-data                      tracked
  Local branches configured for 'git pull':
    master   merges with remote master
    web-data merges with remote web-data
  Local refs configured for 'git push':
    master   pushes to master   (up to date)
    web-data pushes to web-data (up to date)

But if I don’t remember what goes after remote?

What almost anybody who has ever worked with a terminal (or any at least a bit advanced text interface) does in this situation is to hit TAB twice in the hope that the started command sequence will be taken into account and the user will be presented with a list of commands that could follow.

But more often than not that’s not the case with a default git configuration, the user simply gets a list of files in the current directory:

$ git remote [TAB][TAB]
archived_data/                  .git/                           README.md                       
csse_covid_19_data/             .gitignore                      who_covid_19_situation_reports/

This is not what I want, it does not help me complete the command. What is the solution then?

Fortunately enough, this completion functionality is actually provided by the creators of git. You can browse to the official git repository on Github, get the source code, and navigate to contrib/completion directory:

$ mkdir git-source
$ cd git-source
$ git clone https://github.com/git/git.git
...
$ cd git/contrib/completion/

And lo and behold you can see here a few interesting scripts:

$ ls -l
total 108
-rw-r--r-- 1 pavel pavel 73075 May 23 17:05 git-completion.bash
-rw-r--r-- 1 pavel pavel  4801 May 23 16:59 git-completion.tcsh
-rw-r--r-- 1 pavel pavel  6124 May 23 16:59 git-completion.zsh
-rw-r--r-- 1 pavel pavel 16858 May 23 16:59 git-prompt.sh

Based on your shell you choose one and place it in some sensible location (home directory is a good choice, or something like ~/.local/bin). Then you source the script in .bashrc (or in the global version of it if you want to allow this feature for all users):

# git completion if exists
if [[ -f ~/.git-completion.bash ]]; then
    . ~/.git-completion.bash
fi

Technically, you don’t need that extra condition. On the other hand, if you ever (by mistake) delete the file, you won’t be presented with the following annoying message when you open a new terminal window:

bash: /home/pavel/.git-completion.bash: No such file or directory

So I usually make this little effort and source such scripts inside a condition.

Back to the first step. When I now type a part of a git command and then press [TAB] twice, I get a list of commands that could follow:

$ git remote [TAB][TAB]
add            get-url        prune          remove         rename         set-branches   set-head       set-url        show           update

How useful!

It’s even mroe useful for options. git log has more than 100 of them, that’s too many to remember, but with completion, there’s no need to remember them:

$ git log --[TAB][TAB]
Display all 121 possibilities? (y or n)
--abbrev                   --date=                    --full-diff                --minimal                  --no-textconv              --simplify-merges 
--abbrev=                  --date-order               --full-history             --min-parents=             --no-walk                  --since=
--abbrev-commit            --decorate                 --full-index               --name-only                --no-walk=                 --sparse 
--after=                   --decorate=                --graph                    --name-status              --numstat                  --src-prefix=
--all                      --dense                    --grep=                    --no-abbrev-commit         --oneline                  --stat 
--all-match                --diff-algorithm=          --histogram                --no-color                 --parents                  --submodule 
--author=                  --diff-filter=             --ignore-all-space         --no-color-moved           --patch                    --submodule=
--before=                  --dirstat                  --ignore-blank-lines       --no-color-moved-ws        --patch-with-stat          --summary 
--binary                   --dirstat=                 --ignore-cr-at-eol         --no-decorate              --patience                 --tags 
--branches                 --dirstat-by-file          --ignore-space-at-eol      --no-expand-tabs           --pickaxe-all              --text 
--check                    --dirstat-by-file=         --ignore-space-change      --no-ext-diff              --pickaxe-regex            --textconv 
--cherry-mark              --do-walk                  --ignore-submodules        --no-indent-heuristic      --pretty=                  --topo-order 
--cherry-pick              --dst-prefix=              --indent-heuristic         --no-max-parents           --quiet                    --until=
--children                 --exit-code                --inter-hunk-context=      --no-merges                --raw                      --walk-reflogs 
--color                    --expand-tabs              --invert-grep              --no-min-parents           --relative-date            --word-diff 
--color-moved              --expand-tabs=             --left-right               --no-notes                 --remotes                  --word-diff-regex=
--color-moved=             --ext-diff                 --max-age=                 --no-patch                 --reverse                  
--color-moved-ws=          --find-copies-harder       --max-count=               --no-prefix                --root                     
--color-words              --first-parent             --max-parents=             --no-renames               --shortstat                
--committer=               --follow                   --merges                   --not                      --show-signature           
--cumulative               --format=                  --min-age=                 --notes                    --simplify-by-decoration