28bd0e3ba70449132d057edb6b065ba0393056a9
   1#
   2# bash completion support for core Git.
   3#
   4# Copyright (C) 2006 Shawn Pearce
   5# Conceptually based on gitcompletion (http://gitweb.hawaga.org.uk/).
   6#
   7# The contained completion routines provide support for completing:
   8#
   9#    *) local and remote branch names
  10#    *) local and remote tag names
  11#    *) .git/remotes file names
  12#    *) git 'subcommands'
  13#    *) tree paths within 'ref:path/to/file' expressions
  14#
  15# To use these routines:
  16#
  17#    1) Copy this file to somewhere (e.g. ~/.git-completion.sh).
  18#    2) Added the following line to your .bashrc:
  19#        source ~/.git-completion.sh
  20#
  21
  22__gitdir ()
  23{
  24        echo "${__git_dir:-$(git rev-parse --git-dir 2>/dev/null)}"
  25}
  26
  27__git_refs ()
  28{
  29        local cmd i is_hash=y dir="${1:-$(__gitdir)}"
  30        if [ -d "$dir" ]; then
  31                cmd=git-peek-remote
  32        else
  33                cmd=git-ls-remote
  34        fi
  35        for i in $($cmd "$dir" 2>/dev/null); do
  36                case "$is_hash,$i" in
  37                y,*) is_hash=n ;;
  38                n,*^{}) is_hash=y ;;
  39                n,refs/tags/*) is_hash=y; echo "${i#refs/tags/}" ;;
  40                n,refs/heads/*) is_hash=y; echo "${i#refs/heads/}" ;;
  41                n,*) is_hash=y; echo "$i" ;;
  42                esac
  43        done
  44}
  45
  46__git_refs2 ()
  47{
  48        local cmd i is_hash=y dir="${1:-$(__gitdir)}"
  49        if [ -d "$dir" ]; then
  50                cmd=git-peek-remote
  51        else
  52                cmd=git-ls-remote
  53        fi
  54        for i in $($cmd "$dir" 2>/dev/null); do
  55                case "$is_hash,$i" in
  56                y,*) is_hash=n ;;
  57                n,*^{}) is_hash=y ;;
  58                n,refs/tags/*) is_hash=y; echo "${i#refs/tags/}:${i#refs/tags/}" ;;
  59                n,refs/heads/*) is_hash=y; echo "${i#refs/heads/}:${i#refs/heads/}" ;;
  60                n,*) is_hash=y; echo "$i:$i" ;;
  61                esac
  62        done
  63}
  64
  65__git_remotes ()
  66{
  67        local i ngoff IFS=$'\n' d="$(__gitdir)"
  68        shopt -q nullglob || ngoff=1
  69        shopt -s nullglob
  70        for i in "$d/remotes"/*; do
  71                echo ${i#$d/remotes/}
  72        done
  73        [ "$ngoff" ] && shopt -u nullglob
  74        for i in $(git --git-dir="$d" repo-config --list); do
  75                case "$i" in
  76                remote.*.url=*)
  77                        i="${i#remote.}"
  78                        echo "${i/.url=*/}"
  79                        ;;
  80                esac
  81        done
  82}
  83
  84__git_merge_strategies ()
  85{
  86        sed -n "/^all_strategies='/{
  87                s/^all_strategies='//
  88                s/'//
  89                p
  90                q
  91                }" "$(git --exec-path)/git-merge"
  92}
  93
  94__git_complete_file ()
  95{
  96        local pfx ls ref cur="${COMP_WORDS[COMP_CWORD]}"
  97        case "$cur" in
  98        ?*:*)
  99                ref="${cur%%:*}"
 100                cur="${cur#*:}"
 101                case "$cur" in
 102                ?*/*)
 103                        pfx="${cur%/*}"
 104                        cur="${cur##*/}"
 105                        ls="$ref:$pfx"
 106                        pfx="$pfx/"
 107                        ;;
 108                *)
 109                        ls="$ref"
 110                        ;;
 111            esac
 112                COMPREPLY=($(compgen -P "$pfx" \
 113                        -W "$(git --git-dir="$(__gitdir)" ls-tree "$ls" \
 114                                | sed '/^100... blob /s,^.*     ,,
 115                                       /^040000 tree /{
 116                                           s,^.*        ,,
 117                                           s,$,/,
 118                                       }
 119                                       s/^.*    //')" \
 120                        -- "$cur"))
 121                ;;
 122        *)
 123                COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
 124                ;;
 125        esac
 126}
 127
 128__git_aliases ()
 129{
 130        local i IFS=$'\n'
 131        for i in $(git --git-dir="$(__gitdir)" repo-config --list); do
 132                case "$i" in
 133                alias.*)
 134                        i="${i#alias.}"
 135                        echo "${i/=*/}"
 136                        ;;
 137                esac
 138        done
 139}
 140
 141__git_aliased_command ()
 142{
 143        local word cmdline=$(git --git-dir="$(__gitdir)" \
 144                repo-config --get "alias.$1")
 145        for word in $cmdline; do
 146                if [ "${word##-*}" ]; then
 147                        echo $word
 148                        return
 149                fi
 150        done
 151}
 152
 153_git_branch ()
 154{
 155        local cur="${COMP_WORDS[COMP_CWORD]}"
 156        COMPREPLY=($(compgen -W "-l -f -d -D $(__git_refs)" -- "$cur"))
 157}
 158
 159_git_cat_file ()
 160{
 161        local cur="${COMP_WORDS[COMP_CWORD]}"
 162        case "${COMP_WORDS[0]},$COMP_CWORD" in
 163        git-cat-file*,1)
 164                COMPREPLY=($(compgen -W "-p -t blob tree commit tag" -- "$cur"))
 165                ;;
 166        git,2)
 167                COMPREPLY=($(compgen -W "-p -t blob tree commit tag" -- "$cur"))
 168                ;;
 169        *)
 170                __git_complete_file
 171                ;;
 172        esac
 173}
 174
 175_git_checkout ()
 176{
 177        local cur="${COMP_WORDS[COMP_CWORD]}"
 178        COMPREPLY=($(compgen -W "-l -b $(__git_refs)" -- "$cur"))
 179}
 180
 181_git_diff ()
 182{
 183        __git_complete_file
 184}
 185
 186_git_diff_tree ()
 187{
 188        local cur="${COMP_WORDS[COMP_CWORD]}"
 189        COMPREPLY=($(compgen -W "-r -p -M $(__git_refs)" -- "$cur"))
 190}
 191
 192_git_fetch ()
 193{
 194        local cur="${COMP_WORDS[COMP_CWORD]}"
 195
 196        case "${COMP_WORDS[0]},$COMP_CWORD" in
 197        git-fetch*,1)
 198                COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
 199                ;;
 200        git,2)
 201                COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
 202                ;;
 203        *)
 204                case "$cur" in
 205                *:*)
 206                        cur="${cur#*:}"
 207                        COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
 208                        ;;
 209                *)
 210                        local remote
 211                        case "${COMP_WORDS[0]}" in
 212                        git-fetch) remote="${COMP_WORDS[1]}" ;;
 213                        git)       remote="${COMP_WORDS[2]}" ;;
 214                        esac
 215                        COMPREPLY=($(compgen -W "$(__git_refs2 "$remote")" -- "$cur"))
 216                        ;;
 217                esac
 218                ;;
 219        esac
 220}
 221
 222_git_ls_remote ()
 223{
 224        local cur="${COMP_WORDS[COMP_CWORD]}"
 225        COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
 226}
 227
 228_git_ls_tree ()
 229{
 230        __git_complete_file
 231}
 232
 233_git_log ()
 234{
 235        local pfx cur="${COMP_WORDS[COMP_CWORD]}"
 236        case "$cur" in
 237        *...*)
 238                pfx="${cur%...*}..."
 239                cur="${cur#*...}"
 240                COMPREPLY=($(compgen -P "$pfx" -W "$(__git_refs)" -- "$cur"))
 241                ;;
 242        *..*)
 243                pfx="${cur%..*}.."
 244                cur="${cur#*..}"
 245                COMPREPLY=($(compgen -P "$pfx" -W "$(__git_refs)" -- "$cur"))
 246                ;;
 247        *)
 248                COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
 249                ;;
 250        esac
 251}
 252
 253_git_merge ()
 254{
 255        local cur="${COMP_WORDS[COMP_CWORD]}"
 256        case "$cur" in
 257        --*)
 258                COMPREPLY=($(compgen -W "
 259                        --no-commit --no-summary --squash
 260                        " -- "$cur"))
 261                return
 262        esac
 263        if [ $COMP_CWORD -gt 1 -a X-s = "X${COMP_WORDS[COMP_CWORD-1]}" ]
 264        then
 265                COMPREPLY=($(compgen -W "$(__git_merge_strategies)" -- "$cur"))
 266        else
 267                COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
 268        fi
 269}
 270
 271_git_merge_base ()
 272{
 273        local cur="${COMP_WORDS[COMP_CWORD]}"
 274        COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
 275}
 276
 277_git_pull ()
 278{
 279        local cur="${COMP_WORDS[COMP_CWORD]}"
 280
 281        case "${COMP_WORDS[0]},$COMP_CWORD" in
 282        git-pull*,1)
 283                COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
 284                ;;
 285        git,2)
 286                COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
 287                ;;
 288        *)
 289                local remote
 290                case "${COMP_WORDS[0]}" in
 291                git-pull)  remote="${COMP_WORDS[1]}" ;;
 292                git)       remote="${COMP_WORDS[2]}" ;;
 293                esac
 294                COMPREPLY=($(compgen -W "$(__git_refs "$remote")" -- "$cur"))
 295                ;;
 296        esac
 297}
 298
 299_git_push ()
 300{
 301        local cur="${COMP_WORDS[COMP_CWORD]}"
 302
 303        case "${COMP_WORDS[0]},$COMP_CWORD" in
 304        git-push*,1)
 305                COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
 306                ;;
 307        git,2)
 308                COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
 309                ;;
 310        *)
 311                case "$cur" in
 312                *:*)
 313                        local remote
 314                        case "${COMP_WORDS[0]}" in
 315                        git-push)  remote="${COMP_WORDS[1]}" ;;
 316                        git)       remote="${COMP_WORDS[2]}" ;;
 317                        esac
 318                        cur="${cur#*:}"
 319                        COMPREPLY=($(compgen -W "$(__git_refs "$remote")" -- "$cur"))
 320                        ;;
 321                *)
 322                        COMPREPLY=($(compgen -W "$(__git_refs2)" -- "$cur"))
 323                        ;;
 324                esac
 325                ;;
 326        esac
 327}
 328
 329_git_reset ()
 330{
 331        local cur="${COMP_WORDS[COMP_CWORD]}"
 332        local opt="--mixed --hard --soft"
 333        COMPREPLY=($(compgen -W "$opt $(__git_refs)" -- "$cur"))
 334}
 335
 336_git_show ()
 337{
 338        local cur="${COMP_WORDS[COMP_CWORD]}"
 339        COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
 340}
 341
 342_git ()
 343{
 344        local i c=1 command __git_dir
 345
 346        while [ $c -lt $COMP_CWORD ]; do
 347                i="${COMP_WORDS[c]}"
 348                case "$i" in
 349                --git-dir=*) __git_dir="${i#--git-dir=}" ;;
 350                --bare)      __git_dir="." ;;
 351                --version|--help|-p|--paginate) ;;
 352                *) command="$i"; break ;;
 353                esac
 354                c=$((++c))
 355        done
 356
 357        if [ $c -eq $COMP_CWORD -a -z "$command" ]; then
 358                COMPREPLY=($(compgen \
 359                        -W "--git-dir= --version \
 360                                $(git help -a|egrep '^ ') \
 361                            $(__git_aliases)" \
 362                        -- "${COMP_WORDS[COMP_CWORD]}"))
 363                return;
 364        fi
 365
 366        local expansion=$(__git_aliased_command "$command")
 367        [ "$expansion" ] && command="$expansion"
 368
 369        case "$command" in
 370        branch)      _git_branch ;;
 371        cat-file)    _git_cat_file ;;
 372        checkout)    _git_checkout ;;
 373        diff)        _git_diff ;;
 374        diff-tree)   _git_diff_tree ;;
 375        fetch)       _git_fetch ;;
 376        log)         _git_log ;;
 377        ls-remote)   _git_ls_remote ;;
 378        ls-tree)     _git_ls_tree ;;
 379        merge)       _git_merge;;
 380        merge-base)  _git_merge_base ;;
 381        pull)        _git_pull ;;
 382        push)        _git_push ;;
 383        reset)       _git_reset ;;
 384        show)        _git_show ;;
 385        show-branch) _git_log ;;
 386        whatchanged) _git_log ;;
 387        *)           COMPREPLY=() ;;
 388        esac
 389}
 390
 391_gitk ()
 392{
 393        local cur="${COMP_WORDS[COMP_CWORD]}"
 394        COMPREPLY=($(compgen -W "--all $(__git_refs)" -- "$cur"))
 395}
 396
 397complete -o default -o nospace -F _git git
 398complete -o default            -F _gitk gitk
 399complete -o default            -F _git_branch git-branch
 400complete -o default -o nospace -F _git_cat_file git-cat-file
 401complete -o default            -F _git_checkout git-checkout
 402complete -o default -o nospace -F _git_diff git-diff
 403complete -o default            -F _git_diff_tree git-diff-tree
 404complete -o default -o nospace -F _git_fetch git-fetch
 405complete -o default -o nospace -F _git_log git-log
 406complete -o default            -F _git_ls_remote git-ls-remote
 407complete -o default -o nospace -F _git_ls_tree git-ls-tree
 408complete -o default            -F _git_merge git-merge
 409complete -o default            -F _git_merge_base git-merge-base
 410complete -o default -o nospace -F _git_pull git-pull
 411complete -o default -o nospace -F _git_push git-push
 412complete -o default            -F _git_reset git-reset
 413complete -o default            -F _git_show git-show
 414complete -o default -o nospace -F _git_log git-show-branch
 415complete -o default -o nospace -F _git_log git-whatchanged
 416
 417# The following are necessary only for Cygwin, and only are needed
 418# when the user has tab-completed the executable name and consequently
 419# included the '.exe' suffix.
 420#
 421if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then
 422complete -o default -o nospace -F _git git.exe
 423complete -o default            -F _git_branch git-branch.exe
 424complete -o default -o nospace -F _git_cat_file git-cat-file.exe
 425complete -o default -o nospace -F _git_diff git-diff.exe
 426complete -o default -o nospace -F _git_diff_tree git-diff-tree.exe
 427complete -o default -o nospace -F _git_log git-log.exe
 428complete -o default -o nospace -F _git_ls_tree git-ls-tree.exe
 429complete -o default            -F _git_merge_base git-merge-base.exe
 430complete -o default -o nospace -F _git_push git-push.exe
 431complete -o default -o nospace -F _git_log git-show-branch.exe
 432complete -o default -o nospace -F _git_log git-whatchanged.exe
 433fi