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