5582561acde763edb13e30e265be27e73258af9d
   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#    3) Consider changing your PS1 to also show the current branch:
  22#        PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '
  23#
  24#       The argument to __git_ps1 will be displayed only if you
  25#       are currently in a git repository.  The %s token will be
  26#       the name of the current branch.
  27#
  28
  29__gitdir ()
  30{
  31        echo "${__git_dir:-$(git rev-parse --git-dir 2>/dev/null)}"
  32}
  33
  34__git_ps1 ()
  35{
  36        local b="$(git symbolic-ref HEAD 2>/dev/null)"
  37        if [ -n "$b" ]; then
  38                if [ -n "$1" ]; then
  39                        printf "$1" "${b##refs/heads/}"
  40                else
  41                        printf " (%s)" "${b##refs/heads/}"
  42                fi
  43        fi
  44}
  45
  46__git_refs ()
  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/}" ;;
  59                n,refs/heads/*) is_hash=y; echo "${i#refs/heads/}" ;;
  60                n,*) is_hash=y; echo "$i" ;;
  61                esac
  62        done
  63}
  64
  65__git_refs2 ()
  66{
  67        local cmd i is_hash=y dir="${1:-$(__gitdir)}"
  68        if [ -d "$dir" ]; then
  69                cmd=git-peek-remote
  70        else
  71                cmd=git-ls-remote
  72        fi
  73        for i in $($cmd "$dir" 2>/dev/null); do
  74                case "$is_hash,$i" in
  75                y,*) is_hash=n ;;
  76                n,*^{}) is_hash=y ;;
  77                n,refs/tags/*) is_hash=y; echo "${i#refs/tags/}:${i#refs/tags/}" ;;
  78                n,refs/heads/*) is_hash=y; echo "${i#refs/heads/}:${i#refs/heads/}" ;;
  79                n,*) is_hash=y; echo "$i:$i" ;;
  80                esac
  81        done
  82}
  83
  84__git_remotes ()
  85{
  86        local i ngoff IFS=$'\n' d="$(__gitdir)"
  87        shopt -q nullglob || ngoff=1
  88        shopt -s nullglob
  89        for i in "$d/remotes"/*; do
  90                echo ${i#$d/remotes/}
  91        done
  92        [ "$ngoff" ] && shopt -u nullglob
  93        for i in $(git --git-dir="$d" repo-config --list); do
  94                case "$i" in
  95                remote.*.url=*)
  96                        i="${i#remote.}"
  97                        echo "${i/.url=*/}"
  98                        ;;
  99                esac
 100        done
 101}
 102
 103__git_merge_strategies ()
 104{
 105        sed -n "/^all_strategies='/{
 106                s/^all_strategies='//
 107                s/'//
 108                p
 109                q
 110                }" "$(git --exec-path)/git-merge"
 111}
 112
 113__git_complete_file ()
 114{
 115        local pfx ls ref cur="${COMP_WORDS[COMP_CWORD]}"
 116        case "$cur" in
 117        ?*:*)
 118                ref="${cur%%:*}"
 119                cur="${cur#*:}"
 120                case "$cur" in
 121                ?*/*)
 122                        pfx="${cur%/*}"
 123                        cur="${cur##*/}"
 124                        ls="$ref:$pfx"
 125                        pfx="$pfx/"
 126                        ;;
 127                *)
 128                        ls="$ref"
 129                        ;;
 130            esac
 131                COMPREPLY=($(compgen -P "$pfx" \
 132                        -W "$(git --git-dir="$(__gitdir)" ls-tree "$ls" \
 133                                | sed '/^100... blob /s,^.*     ,,
 134                                       /^040000 tree /{
 135                                           s,^.*        ,,
 136                                           s,$,/,
 137                                       }
 138                                       s/^.*    //')" \
 139                        -- "$cur"))
 140                ;;
 141        *)
 142                COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
 143                ;;
 144        esac
 145}
 146
 147__git_complete_revlist ()
 148{
 149        local pfx cur="${COMP_WORDS[COMP_CWORD]}"
 150        case "$cur" in
 151        *...*)
 152                pfx="${cur%...*}..."
 153                cur="${cur#*...}"
 154                COMPREPLY=($(compgen -P "$pfx" -W "$(__git_refs)" -- "$cur"))
 155                ;;
 156        *..*)
 157                pfx="${cur%..*}.."
 158                cur="${cur#*..}"
 159                COMPREPLY=($(compgen -P "$pfx" -W "$(__git_refs)" -- "$cur"))
 160                ;;
 161        *)
 162                COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
 163                ;;
 164        esac
 165}
 166
 167__git_commands ()
 168{
 169        local i IFS=" "$'\n'
 170        for i in $(git help -a|egrep '^ ')
 171        do
 172                case $i in
 173                check-ref-format) : plumbing;;
 174                commit-tree)      : plumbing;;
 175                convert-objects)  : plumbing;;
 176                cvsserver)        : daemon;;
 177                daemon)           : daemon;;
 178                fetch-pack)       : plumbing;;
 179                hash-object)      : plumbing;;
 180                http-*)           : transport;;
 181                index-pack)       : plumbing;;
 182                local-fetch)      : plumbing;;
 183                mailinfo)         : plumbing;;
 184                mailsplit)        : plumbing;;
 185                merge-*)          : plumbing;;
 186                mktree)           : plumbing;;
 187                mktag)            : plumbing;;
 188                pack-objects)     : plumbing;;
 189                pack-redundant)   : plumbing;;
 190                pack-refs)        : plumbing;;
 191                parse-remote)     : plumbing;;
 192                patch-id)         : plumbing;;
 193                peek-remote)      : plumbing;;
 194                read-tree)        : plumbing;;
 195                receive-pack)     : plumbing;;
 196                rerere)           : plumbing;;
 197                rev-list)         : plumbing;;
 198                rev-parse)        : plumbing;;
 199                runstatus)        : plumbing;;
 200                sh-setup)         : internal;;
 201                shell)            : daemon;;
 202                send-pack)        : plumbing;;
 203                show-index)       : plumbing;;
 204                ssh-*)            : transport;;
 205                stripspace)       : plumbing;;
 206                symbolic-ref)     : plumbing;;
 207                unpack-file)      : plumbing;;
 208                unpack-objects)   : plumbing;;
 209                update-ref)       : plumbing;;
 210                update-server-info) : daemon;;
 211                upload-archive)   : plumbing;;
 212                upload-pack)      : plumbing;;
 213                write-tree)       : plumbing;;
 214                *) echo $i;;
 215                esac
 216        done
 217}
 218
 219__git_aliases ()
 220{
 221        local i IFS=$'\n'
 222        for i in $(git --git-dir="$(__gitdir)" repo-config --list); do
 223                case "$i" in
 224                alias.*)
 225                        i="${i#alias.}"
 226                        echo "${i/=*/}"
 227                        ;;
 228                esac
 229        done
 230}
 231
 232__git_aliased_command ()
 233{
 234        local word cmdline=$(git --git-dir="$(__gitdir)" \
 235                repo-config --get "alias.$1")
 236        for word in $cmdline; do
 237                if [ "${word##-*}" ]; then
 238                        echo $word
 239                        return
 240                fi
 241        done
 242}
 243
 244_git_branch ()
 245{
 246        local cur="${COMP_WORDS[COMP_CWORD]}"
 247        COMPREPLY=($(compgen -W "-l -f -d -D $(__git_refs)" -- "$cur"))
 248}
 249
 250_git_cat_file ()
 251{
 252        local cur="${COMP_WORDS[COMP_CWORD]}"
 253        case "${COMP_WORDS[0]},$COMP_CWORD" in
 254        git-cat-file*,1)
 255                COMPREPLY=($(compgen -W "-p -t blob tree commit tag" -- "$cur"))
 256                ;;
 257        git,2)
 258                COMPREPLY=($(compgen -W "-p -t blob tree commit tag" -- "$cur"))
 259                ;;
 260        *)
 261                __git_complete_file
 262                ;;
 263        esac
 264}
 265
 266_git_checkout ()
 267{
 268        local cur="${COMP_WORDS[COMP_CWORD]}"
 269        COMPREPLY=($(compgen -W "-l -b $(__git_refs)" -- "$cur"))
 270}
 271
 272_git_cherry_pick ()
 273{
 274        local cur="${COMP_WORDS[COMP_CWORD]}"
 275        case "$cur" in
 276        --*)
 277                COMPREPLY=($(compgen -W "
 278                        --edit --no-commit
 279                        " -- "$cur"))
 280                ;;
 281        *)
 282                COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
 283                ;;
 284        esac
 285}
 286
 287_git_diff ()
 288{
 289        __git_complete_file
 290}
 291
 292_git_diff_tree ()
 293{
 294        local cur="${COMP_WORDS[COMP_CWORD]}"
 295        COMPREPLY=($(compgen -W "-r -p -M $(__git_refs)" -- "$cur"))
 296}
 297
 298_git_fetch ()
 299{
 300        local cur="${COMP_WORDS[COMP_CWORD]}"
 301
 302        case "${COMP_WORDS[0]},$COMP_CWORD" in
 303        git-fetch*,1)
 304                COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
 305                ;;
 306        git,2)
 307                COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
 308                ;;
 309        *)
 310                case "$cur" in
 311                *:*)
 312                        cur="${cur#*:}"
 313                        COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
 314                        ;;
 315                *)
 316                        local remote
 317                        case "${COMP_WORDS[0]}" in
 318                        git-fetch) remote="${COMP_WORDS[1]}" ;;
 319                        git)       remote="${COMP_WORDS[2]}" ;;
 320                        esac
 321                        COMPREPLY=($(compgen -W "$(__git_refs2 "$remote")" -- "$cur"))
 322                        ;;
 323                esac
 324                ;;
 325        esac
 326}
 327
 328_git_format_patch ()
 329{
 330        local cur="${COMP_WORDS[COMP_CWORD]}"
 331        case "$cur" in
 332        --*)
 333                COMPREPLY=($(compgen -W "
 334                        --stdout --attach --thread
 335                        --output-directory
 336                        --numbered --start-number
 337                        --keep-subject
 338                        --signoff
 339                        --in-reply-to=
 340                        --full-index --binary
 341                        " -- "$cur"))
 342                return
 343                ;;
 344        esac
 345        __git_complete_revlist
 346}
 347
 348_git_ls_remote ()
 349{
 350        local cur="${COMP_WORDS[COMP_CWORD]}"
 351        COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
 352}
 353
 354_git_ls_tree ()
 355{
 356        __git_complete_file
 357}
 358
 359_git_log ()
 360{
 361        __git_complete_revlist
 362}
 363
 364_git_merge ()
 365{
 366        local cur="${COMP_WORDS[COMP_CWORD]}"
 367        case "$cur" in
 368        --*)
 369                COMPREPLY=($(compgen -W "
 370                        --no-commit --no-summary --squash
 371                        " -- "$cur"))
 372                return
 373        esac
 374        if [ $COMP_CWORD -gt 1 -a X-s = "X${COMP_WORDS[COMP_CWORD-1]}" ]
 375        then
 376                COMPREPLY=($(compgen -W "$(__git_merge_strategies)" -- "$cur"))
 377        else
 378                COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
 379        fi
 380}
 381
 382_git_merge_base ()
 383{
 384        local cur="${COMP_WORDS[COMP_CWORD]}"
 385        COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
 386}
 387
 388_git_name_rev ()
 389{
 390        local cur="${COMP_WORDS[COMP_CWORD]}"
 391        COMPREPLY=($(compgen -W "--tags --all --stdin" -- "$cur"))
 392}
 393
 394_git_pull ()
 395{
 396        local cur="${COMP_WORDS[COMP_CWORD]}"
 397
 398        case "${COMP_WORDS[0]},$COMP_CWORD" in
 399        git-pull*,1)
 400                COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
 401                ;;
 402        git,2)
 403                COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
 404                ;;
 405        *)
 406                local remote
 407                case "${COMP_WORDS[0]}" in
 408                git-pull)  remote="${COMP_WORDS[1]}" ;;
 409                git)       remote="${COMP_WORDS[2]}" ;;
 410                esac
 411                COMPREPLY=($(compgen -W "$(__git_refs "$remote")" -- "$cur"))
 412                ;;
 413        esac
 414}
 415
 416_git_push ()
 417{
 418        local cur="${COMP_WORDS[COMP_CWORD]}"
 419
 420        case "${COMP_WORDS[0]},$COMP_CWORD" in
 421        git-push*,1)
 422                COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
 423                ;;
 424        git,2)
 425                COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
 426                ;;
 427        *)
 428                case "$cur" in
 429                *:*)
 430                        local remote
 431                        case "${COMP_WORDS[0]}" in
 432                        git-push)  remote="${COMP_WORDS[1]}" ;;
 433                        git)       remote="${COMP_WORDS[2]}" ;;
 434                        esac
 435                        cur="${cur#*:}"
 436                        COMPREPLY=($(compgen -W "$(__git_refs "$remote")" -- "$cur"))
 437                        ;;
 438                *)
 439                        COMPREPLY=($(compgen -W "$(__git_refs2)" -- "$cur"))
 440                        ;;
 441                esac
 442                ;;
 443        esac
 444}
 445
 446_git_reset ()
 447{
 448        local cur="${COMP_WORDS[COMP_CWORD]}"
 449        local opt="--mixed --hard --soft"
 450        COMPREPLY=($(compgen -W "$opt $(__git_refs)" -- "$cur"))
 451}
 452
 453_git_show ()
 454{
 455        local cur="${COMP_WORDS[COMP_CWORD]}"
 456        COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
 457}
 458
 459_git ()
 460{
 461        local i c=1 command __git_dir
 462
 463        while [ $c -lt $COMP_CWORD ]; do
 464                i="${COMP_WORDS[c]}"
 465                case "$i" in
 466                --git-dir=*) __git_dir="${i#--git-dir=}" ;;
 467                --bare)      __git_dir="." ;;
 468                --version|--help|-p|--paginate) ;;
 469                *) command="$i"; break ;;
 470                esac
 471                c=$((++c))
 472        done
 473
 474        if [ $c -eq $COMP_CWORD -a -z "$command" ]; then
 475                COMPREPLY=($(compgen -W "
 476                        --git-dir= --version --exec-path
 477                        $(__git_commands)
 478                        $(__git_aliases)
 479                        " -- "${COMP_WORDS[COMP_CWORD]}"))
 480                return;
 481        fi
 482
 483        local expansion=$(__git_aliased_command "$command")
 484        [ "$expansion" ] && command="$expansion"
 485
 486        case "$command" in
 487        branch)      _git_branch ;;
 488        cat-file)    _git_cat_file ;;
 489        checkout)    _git_checkout ;;
 490        cherry-pick) _git_cherry_pick ;;
 491        diff)        _git_diff ;;
 492        diff-tree)   _git_diff_tree ;;
 493        fetch)       _git_fetch ;;
 494        format-patch) _git_format_patch ;;
 495        log)         _git_log ;;
 496        ls-remote)   _git_ls_remote ;;
 497        ls-tree)     _git_ls_tree ;;
 498        merge)       _git_merge;;
 499        merge-base)  _git_merge_base ;;
 500        name-rev)    _git_name_rev ;;
 501        pull)        _git_pull ;;
 502        push)        _git_push ;;
 503        reset)       _git_reset ;;
 504        show)        _git_show ;;
 505        show-branch) _git_log ;;
 506        whatchanged) _git_log ;;
 507        *)           COMPREPLY=() ;;
 508        esac
 509}
 510
 511_gitk ()
 512{
 513        local cur="${COMP_WORDS[COMP_CWORD]}"
 514        COMPREPLY=($(compgen -W "--all $(__git_refs)" -- "$cur"))
 515}
 516
 517complete -o default -o nospace -F _git git
 518complete -o default            -F _gitk gitk
 519complete -o default            -F _git_branch git-branch
 520complete -o default -o nospace -F _git_cat_file git-cat-file
 521complete -o default            -F _git_checkout git-checkout
 522complete -o default            -F _git_cherry_pick git-cherry-pick
 523complete -o default -o nospace -F _git_diff git-diff
 524complete -o default            -F _git_diff_tree git-diff-tree
 525complete -o default -o nospace -F _git_fetch git-fetch
 526complete -o default -o nospace -F _git_format_patch git-format-patch
 527complete -o default -o nospace -F _git_log git-log
 528complete -o default            -F _git_ls_remote git-ls-remote
 529complete -o default -o nospace -F _git_ls_tree git-ls-tree
 530complete -o default            -F _git_merge git-merge
 531complete -o default            -F _git_merge_base git-merge-base
 532complete -o default            -F _git_name_rev git-name-rev
 533complete -o default -o nospace -F _git_pull git-pull
 534complete -o default -o nospace -F _git_push git-push
 535complete -o default            -F _git_reset git-reset
 536complete -o default            -F _git_show git-show
 537complete -o default -o nospace -F _git_log git-show-branch
 538complete -o default -o nospace -F _git_log git-whatchanged
 539
 540# The following are necessary only for Cygwin, and only are needed
 541# when the user has tab-completed the executable name and consequently
 542# included the '.exe' suffix.
 543#
 544if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then
 545complete -o default -o nospace -F _git git.exe
 546complete -o default            -F _git_branch git-branch.exe
 547complete -o default -o nospace -F _git_cat_file git-cat-file.exe
 548complete -o default -o nospace -F _git_diff git-diff.exe
 549complete -o default -o nospace -F _git_diff_tree git-diff-tree.exe
 550complete -o default -o nospace -F _git_format_patch git-format-patch.exe
 551complete -o default -o nospace -F _git_log git-log.exe
 552complete -o default -o nospace -F _git_ls_tree git-ls-tree.exe
 553complete -o default            -F _git_merge_base git-merge-base.exe
 554complete -o default            -F _git_name_rev git-name-rev.exe
 555complete -o default -o nospace -F _git_push git-push.exe
 556complete -o default -o nospace -F _git_log git-show-branch.exe
 557complete -o default -o nospace -F _git_log git-whatchanged.exe
 558fi