f258f2f03e4a1ce2986c4d2fb645f7a895a90d3d
   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_complete_file ()
  85{
  86        local cur="${COMP_WORDS[COMP_CWORD]}"
  87        case "$cur" in
  88        ?*:*)
  89                local pfx ls ref="$(echo "$cur" | sed 's,:.*$,,')"
  90                cur="$(echo "$cur" | sed 's,^.*:,,')"
  91                case "$cur" in
  92                ?*/*)
  93                        pfx="$(echo "$cur" | sed 's,/[^/]*$,,')"
  94                        cur="$(echo "$cur" | sed 's,^.*/,,')"
  95                        ls="$ref:$pfx"
  96                        pfx="$pfx/"
  97                        ;;
  98                *)
  99                        ls="$ref"
 100                        ;;
 101            esac
 102                COMPREPLY=($(compgen -P "$pfx" \
 103                        -W "$(git --git-dir="$(__gitdir)" ls-tree "$ls" \
 104                                | sed '/^100... blob /s,^.*     ,,
 105                                       /^040000 tree /{
 106                                           s,^.*        ,,
 107                                           s,$,/,
 108                                       }
 109                                       s/^.*    //')" \
 110                        -- "$cur"))
 111                ;;
 112        *)
 113                COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
 114                ;;
 115        esac
 116}
 117
 118__git_aliases ()
 119{
 120        local i IFS=$'\n'
 121        for i in $(git --git-dir="$(__gitdir)" repo-config --list); do
 122                case "$i" in
 123                alias.*)
 124                        i="${i#alias.}"
 125                        echo "${i/=*/}"
 126                        ;;
 127                esac
 128        done
 129}
 130
 131__git_aliased_command ()
 132{
 133        local word cmdline=$(git --git-dir="$(__gitdir)" \
 134                repo-config --get "alias.$1")
 135        for word in $cmdline; do
 136                if [ "${word##-*}" ]; then
 137                        echo $word
 138                        return
 139                fi
 140        done
 141}
 142
 143_git_branch ()
 144{
 145        local cur="${COMP_WORDS[COMP_CWORD]}"
 146        COMPREPLY=($(compgen -W "-l -f -d -D $(__git_refs)" -- "$cur"))
 147}
 148
 149_git_cat_file ()
 150{
 151        local cur="${COMP_WORDS[COMP_CWORD]}"
 152        case "${COMP_WORDS[0]},$COMP_CWORD" in
 153        git-cat-file*,1)
 154                COMPREPLY=($(compgen -W "-p -t blob tree commit tag" -- "$cur"))
 155                ;;
 156        git,2)
 157                COMPREPLY=($(compgen -W "-p -t blob tree commit tag" -- "$cur"))
 158                ;;
 159        *)
 160                __git_complete_file
 161                ;;
 162        esac
 163}
 164
 165_git_checkout ()
 166{
 167        local cur="${COMP_WORDS[COMP_CWORD]}"
 168        COMPREPLY=($(compgen -W "-l -b $(__git_refs)" -- "$cur"))
 169}
 170
 171_git_diff ()
 172{
 173        __git_complete_file
 174}
 175
 176_git_diff_tree ()
 177{
 178        local cur="${COMP_WORDS[COMP_CWORD]}"
 179        COMPREPLY=($(compgen -W "-r -p -M $(__git_refs)" -- "$cur"))
 180}
 181
 182_git_fetch ()
 183{
 184        local cur="${COMP_WORDS[COMP_CWORD]}"
 185
 186        case "${COMP_WORDS[0]},$COMP_CWORD" in
 187        git-fetch*,1)
 188                COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
 189                ;;
 190        git,2)
 191                COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
 192                ;;
 193        *)
 194                case "$cur" in
 195                *:*)
 196                cur=$(echo "$cur" | sed 's/^.*://')
 197                        COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
 198                        ;;
 199                *)
 200                        local remote
 201                        case "${COMP_WORDS[0]}" in
 202                        git-fetch) remote="${COMP_WORDS[1]}" ;;
 203                        git)       remote="${COMP_WORDS[2]}" ;;
 204                        esac
 205                        COMPREPLY=($(compgen -W "$(__git_refs2 "$remote")" -- "$cur"))
 206                        ;;
 207                esac
 208                ;;
 209        esac
 210}
 211
 212_git_ls_remote ()
 213{
 214        local cur="${COMP_WORDS[COMP_CWORD]}"
 215        COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
 216}
 217
 218_git_ls_tree ()
 219{
 220        __git_complete_file
 221}
 222
 223_git_log ()
 224{
 225        local cur="${COMP_WORDS[COMP_CWORD]}"
 226        case "$cur" in
 227        *..*)
 228                local pfx=$(echo "$cur" | sed 's/\.\..*$/../')
 229                cur=$(echo "$cur" | sed 's/^.*\.\.//')
 230                COMPREPLY=($(compgen -P "$pfx" -W "$(__git_refs)" -- "$cur"))
 231                ;;
 232        *)
 233                COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
 234                ;;
 235        esac
 236}
 237
 238_git_merge_base ()
 239{
 240        local cur="${COMP_WORDS[COMP_CWORD]}"
 241        COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
 242}
 243
 244_git_pull ()
 245{
 246        local cur="${COMP_WORDS[COMP_CWORD]}"
 247
 248        case "${COMP_WORDS[0]},$COMP_CWORD" in
 249        git-pull*,1)
 250                COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
 251                ;;
 252        git,2)
 253                COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
 254                ;;
 255        *)
 256                local remote
 257                case "${COMP_WORDS[0]}" in
 258                git-pull)  remote="${COMP_WORDS[1]}" ;;
 259                git)       remote="${COMP_WORDS[2]}" ;;
 260                esac
 261                COMPREPLY=($(compgen -W "$(__git_refs "$remote")" -- "$cur"))
 262                ;;
 263        esac
 264}
 265
 266_git_push ()
 267{
 268        local cur="${COMP_WORDS[COMP_CWORD]}"
 269
 270        case "${COMP_WORDS[0]},$COMP_CWORD" in
 271        git-push*,1)
 272                COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
 273                ;;
 274        git,2)
 275                COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
 276                ;;
 277        *)
 278                case "$cur" in
 279                *:*)
 280                        local remote
 281                        case "${COMP_WORDS[0]}" in
 282                        git-push)  remote="${COMP_WORDS[1]}" ;;
 283                        git)       remote="${COMP_WORDS[2]}" ;;
 284                        esac
 285                cur=$(echo "$cur" | sed 's/^.*://')
 286                        COMPREPLY=($(compgen -W "$(__git_refs "$remote")" -- "$cur"))
 287                        ;;
 288                *)
 289                        COMPREPLY=($(compgen -W "$(__git_refs2)" -- "$cur"))
 290                        ;;
 291                esac
 292                ;;
 293        esac
 294}
 295
 296_git_reset ()
 297{
 298        local cur="${COMP_WORDS[COMP_CWORD]}"
 299        local opt="--mixed --hard --soft"
 300        COMPREPLY=($(compgen -W "$opt $(__git_refs)" -- "$cur"))
 301}
 302
 303_git_show ()
 304{
 305        local cur="${COMP_WORDS[COMP_CWORD]}"
 306        COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
 307}
 308
 309_git ()
 310{
 311        local i c=1 command __git_dir
 312
 313        while [ $c -lt $COMP_CWORD ]; do
 314                i="${COMP_WORDS[c]}"
 315                case "$i" in
 316                --git-dir=*) __git_dir="${i#--git-dir=}" ;;
 317                --bare)      __git_dir="." ;;
 318                --version|--help|-p|--paginate) ;;
 319                *) command="$i"; break ;;
 320                esac
 321                c=$((++c))
 322        done
 323
 324        if [ $c -eq $COMP_CWORD -a -z "$command" ]; then
 325                COMPREPLY=($(compgen \
 326                        -W "--git-dir= --version \
 327                                $(git help -a|egrep '^ ') \
 328                            $(__git_aliases)" \
 329                        -- "${COMP_WORDS[COMP_CWORD]}"))
 330                return;
 331        fi
 332
 333        local expansion=$(__git_aliased_command "$command")
 334        [ "$expansion" ] && command="$expansion"
 335
 336        case "$command" in
 337        branch)      _git_branch ;;
 338        cat-file)    _git_cat_file ;;
 339        checkout)    _git_checkout ;;
 340        diff)        _git_diff ;;
 341        diff-tree)   _git_diff_tree ;;
 342        fetch)       _git_fetch ;;
 343        log)         _git_log ;;
 344        ls-remote)   _git_ls_remote ;;
 345        ls-tree)     _git_ls_tree ;;
 346        merge-base)  _git_merge_base ;;
 347        pull)        _git_pull ;;
 348        push)        _git_push ;;
 349        reset)       _git_reset ;;
 350        show)        _git_show ;;
 351        show-branch) _git_log ;;
 352        whatchanged) _git_log ;;
 353        *)           COMPREPLY=() ;;
 354        esac
 355}
 356
 357_gitk ()
 358{
 359        local cur="${COMP_WORDS[COMP_CWORD]}"
 360        COMPREPLY=($(compgen -W "--all $(__git_refs)" -- "$cur"))
 361}
 362
 363complete -o default -o nospace -F _git git
 364complete -o default            -F _gitk gitk
 365complete -o default            -F _git_branch git-branch
 366complete -o default -o nospace -F _git_cat_file git-cat-file
 367complete -o default            -F _git_checkout git-checkout
 368complete -o default -o nospace -F _git_diff git-diff
 369complete -o default            -F _git_diff_tree git-diff-tree
 370complete -o default -o nospace -F _git_fetch git-fetch
 371complete -o default -o nospace -F _git_log git-log
 372complete -o default            -F _git_ls_remote git-ls-remote
 373complete -o default -o nospace -F _git_ls_tree git-ls-tree
 374complete -o default            -F _git_merge_base git-merge-base
 375complete -o default -o nospace -F _git_pull git-pull
 376complete -o default -o nospace -F _git_push git-push
 377complete -o default            -F _git_reset git-reset
 378complete -o default            -F _git_show git-show
 379complete -o default -o nospace -F _git_log git-show-branch
 380complete -o default -o nospace -F _git_log git-whatchanged
 381
 382# The following are necessary only for Cygwin, and only are needed
 383# when the user has tab-completed the executable name and consequently
 384# included the '.exe' suffix.
 385#
 386if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then
 387complete -o default -o nospace -F _git git.exe
 388complete -o default            -F _git_branch git-branch.exe
 389complete -o default -o nospace -F _git_cat_file git-cat-file.exe
 390complete -o default -o nospace -F _git_diff git-diff.exe
 391complete -o default -o nospace -F _git_diff_tree git-diff-tree.exe
 392complete -o default -o nospace -F _git_log git-log.exe
 393complete -o default -o nospace -F _git_ls_tree git-ls-tree.exe
 394complete -o default            -F _git_merge_base git-merge-base.exe
 395complete -o default -o nospace -F _git_push git-push.exe
 396complete -o default -o nospace -F _git_log git-show-branch.exe
 397complete -o default -o nospace -F _git_log git-whatchanged.exe
 398fi