#!bash
#
# bash completion support for core Git.
#
# Copyright (C) 2006,2007 Shawn O. Pearce <spearce@spearce.org>
# Conceptually based on gitcompletion (http://gitweb.hawaga.org.uk/).
# Distributed under the GNU General Public License, version 2.0.
#
# The contained completion routines provide support for completing:
#
#    *) local and remote branch names
#    *) local and remote tag names
#    *) .git/remotes file names
#    *) git 'subcommands'
#    *) tree paths within 'ref:path/to/file' expressions
#    *) common --long-options
#
# To use these routines:
#
#    1) Copy this file to somewhere (e.g. ~/.git-completion.sh).
#    2) Added the following line to your .bashrc:
#        source ~/.git-completion.sh
#
#    3) Consider changing your PS1 to also show the current branch:
#        PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '
#
#       The argument to __git_ps1 will be displayed only if you
#       are currently in a git repository.  The %s token will be
#       the name of the current branch.
#
#       In addition, if you set GIT_PS1_SHOWDIRTYSTATE to a nonempty
#       value, unstaged (*) and staged (+) changes will be shown next
#       to the branch name.  You can configure this per-repository
#       with the bash.showDirtyState variable, which defaults to true
#       once GIT_PS1_SHOWDIRTYSTATE is enabled.
#
#       You can also see if currently something is stashed, by setting
#       GIT_PS1_SHOWSTASHSTATE to a nonempty value. If something is stashed,
#       then a '$' will be shown next to the branch name.
#
#       If you would like to see if there're untracked files, then you can
#       set GIT_PS1_SHOWUNTRACKEDFILES to a nonempty value. If there're
#       untracked files, then a '%' will be shown next to the branch name.
#
# To submit patches:
#
#    *) Read Documentation/SubmittingPatches
#    *) Send all patches to the current maintainer:
#
#       "Shawn O. Pearce" <spearce@spearce.org>
#
#    *) Always CC the Git mailing list:
#
#       git@vger.kernel.org
#

case "$COMP_WORDBREAKS" in
*:*) : great ;;
*)   COMP_WORDBREAKS="$COMP_WORDBREAKS:"
esac

# __gitdir accepts 0 or 1 arguments (i.e., location)
# returns location of .git repo
__gitdir ()
{
  if [ -z "${1-}" ]; then
    if [ -n "${__git_dir-}" ]; then
      echo "$__git_dir"
    elif [ -d .git ]; then
      echo .git
    else
      git rev-parse --git-dir 2>/dev/null
    fi
  elif [ -d "$1/.git" ]; then
    echo "$1/.git"
  else
    echo "$1"
  fi
}

# __git_ps1 accepts 0 or 1 arguments (i.e., format string)
# returns text to add to bash PS1 prompt (includes branch name)
__git_ps1 ()
{
  local g="$(__gitdir)"
  if [ -n "$g" ]; then
    local r
    local b
    if [ -f "$g/rebase-merge/interactive" ]; then
      r="|REBASE-i"
      b="$(cat "$g/rebase-merge/head-name")"
    elif [ -d "$g/rebase-merge" ]; then
      r="|REBASE-m"
      b="$(cat "$g/rebase-merge/head-name")"
    else
      if [ -d "$g/rebase-apply" ]; then
        if [ -f "$g/rebase-apply/rebasing" ]; then
          r="|REBASE"
        elif [ -f "$g/rebase-apply/applying" ]; then
          r="|AM"
        else
          r="|AM/REBASE"
        fi
      elif [ -f "$g/MERGE_HEAD" ]; then
        r="|MERGING"
      elif [ -f "$g/BISECT_LOG" ]; then
        r="|BISECTING"
      fi

      b="$(git symbolic-ref HEAD 2>/dev/null)" || {

        b="$(
        case "${GIT_PS1_DESCRIBE_STYLE-}" in
        (contains)
          git describe --contains HEAD ;;
        (branch)
          git describe --contains --all HEAD ;;
        (describe)
          git describe HEAD ;;
        (* | default)
          git describe --exact-match HEAD ;;
        esac 2>/dev/null)" ||

        b="$(cut -c1-7 "$g/HEAD" 2>/dev/null)..." ||
        b="unknown"
        b="($b)"
      }
    fi

    local w
    local i
    local s
    local u
    local c

    if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then
      if [ "true" = "$(git rev-parse --is-bare-repository 2>/dev/null)" ]; then
        c="BARE:"
      else
        b="GIT_DIR!"
      fi
    elif [ "true" = "$(git rev-parse --is-inside-work-tree 2>/dev/null)" ]; then
      if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ]; then
        if [ "$(git config --bool bash.showDirtyState)" != "false" ]; then
          git diff --no-ext-diff --quiet --exit-code || w="*"
          if git rev-parse --quiet --verify HEAD >/dev/null; then
            git diff-index --cached --quiet HEAD -- || i="+"
          else
            i="#"
          fi
        fi
      fi
      if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ]; then
              git rev-parse --verify refs/stash >/dev/null 2>&1 && s="$"
      fi

      if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ]; then
         if [ -n "$(git ls-files --others --exclude-standard)" ]; then
            u="%"
         fi
      fi
    fi

    local f="$w$i$s$u"
    printf "${1:- (%s)}" "$c${b##refs/heads/}${f:+ $f}$r"
  fi
}

# __gitcomp_1 requires 2 arguments
__gitcomp_1 ()
{
  local c IFS=' '$'\t'$'\n'
  for c in $1; do
    case "$c$2" in
    --*=*) printf %s$'\n' "$c$2" ;;
    *.)    printf %s$'\n' "$c$2" ;;
    *)     printf %s$'\n' "$c$2 " ;;
    esac
  done
}

# __gitcomp accepts 1, 2, 3, or 4 arguments
# generates completion reply with compgen
__gitcomp ()
{
  local cur="${COMP_WORDS[COMP_CWORD]}"
  if [ $# -gt 2 ]; then
    cur="$3"
  fi
  case "$cur" in
  --*=)
    COMPREPLY=()
    ;;
  *)
    local IFS=$'\n'
    COMPREPLY=($(compgen -P "${2-}" \
      -W "$(__gitcomp_1 "${1-}" "${4-}")" \
      -- "$cur"))
    ;;
  esac
}

# __git_heads accepts 0 or 1 arguments (to pass to __gitdir)
__git_heads ()
{
  local cmd i is_hash=y dir="$(__gitdir "${1-}")"
  if [ -d "$dir" ]; then
    git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
      refs/heads
    return
  fi
  for i in $(git ls-remote "${1-}" 2>/dev/null); do
    case "$is_hash,$i" in
    y,*) is_hash=n ;;
    n,*^{}) is_hash=y ;;
    n,refs/heads/*) is_hash=y; echo "${i#refs/heads/}" ;;
    n,*) is_hash=y; echo "$i" ;;
    esac
  done
}

# __git_tags accepts 0 or 1 arguments (to pass to __gitdir)
__git_tags ()
{
  local cmd i is_hash=y dir="$(__gitdir "${1-}")"
  if [ -d "$dir" ]; then
    git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
      refs/tags
    return
  fi
  for i in $(git ls-remote "${1-}" 2>/dev/null); do
    case "$is_hash,$i" in
    y,*) is_hash=n ;;
    n,*^{}) is_hash=y ;;
    n,refs/tags/*) is_hash=y; echo "${i#refs/tags/}" ;;
    n,*) is_hash=y; echo "$i" ;;
    esac
  done
}

# __git_refs accepts 0 or 1 arguments (to pass to __gitdir)
__git_refs ()
{
  local i is_hash=y dir="$(__gitdir "${1-}")"
  local cur="${COMP_WORDS[COMP_CWORD]}" format refs
  if [ -d "$dir" ]; then
    case "$cur" in
    refs|refs/*)
      format="refname"
      refs="${cur%/*}"
      ;;
    *)
      if [ -e "$dir/HEAD" ]; then echo HEAD; fi
      format="refname:short"
      refs="refs/tags refs/heads refs/remotes"
      ;;
    esac
    git --git-dir="$dir" for-each-ref --format="%($format)" \
      $refs
    return
  fi
  for i in $(git ls-remote "$dir" 2>/dev/null); do
    case "$is_hash,$i" in
    y,*) is_hash=n ;;
    n,*^{}) is_hash=y ;;
    n,refs/tags/*) is_hash=y; echo "${i#refs/tags/}" ;;
    n,refs/heads/*) is_hash=y; echo "${i#refs/heads/}" ;;
    n,refs/remotes/*) is_hash=y; echo "${i#refs/remotes/}" ;;
    n,*) is_hash=y; echo "$i" ;;
    esac
  done
}

# __git_refs2 requires 1 argument (to pass to __git_refs)
__git_refs2 ()
{
  local i
  for i in $(__git_refs "$1"); do
    echo "$i:$i"
  done
}

# __git_refs_remotes requires 1 argument (to pass to ls-remote)
__git_refs_remotes ()
{
  local cmd i is_hash=y
  for i in $(git ls-remote "$1" 2>/dev/null); do
    case "$is_hash,$i" in
    n,refs/heads/*)
      is_hash=y
      echo "$i:refs/remotes/$1/${i#refs/heads/}"
      ;;
    y,*) is_hash=n ;;
    n,*^{}) is_hash=y ;;
    n,refs/tags/*) is_hash=y;;
    n,*) is_hash=y; ;;
    esac
  done
}

__git_remotes ()
{
  local i ngoff IFS=$'\n' d="$(__gitdir)"
  shopt -q nullglob || ngoff=1
  shopt -s nullglob
  for i in "$d/remotes"/*; do
    echo ${i#$d/remotes/}
  done
  [ "$ngoff" ] && shopt -u nullglob
  for i in $(git --git-dir="$d" config --get-regexp 'remote\..*\.url' 2>/dev/null); do
    i="${i#remote.}"
    echo "${i/.url*/}"
  done
}

__git_list_merge_strategies ()
{
  git merge -s help 2>&1 |
  sed -n -e '/[Aa]vailable strategies are: /,/^$/{
    s/\.$//
    s/.*://
    s/^[  ]*//
    s/[   ]*$//
    p
  }'
}

__git_merge_strategies=
# 'git merge -s help' (and thus detection of the merge strategy
# list) fails, unfortunately, if run outside of any git working
# tree.  __git_merge_strategies is set to the empty string in
# that case, and the detection will be repeated the next time it
# is needed.
__git_compute_merge_strategies ()
{
  : ${__git_merge_strategies:=$(__git_list_merge_strategies)}
}

__git_complete_file ()
{
  local pfx ls ref cur="${COMP_WORDS[COMP_CWORD]}"
  case "$cur" in
  ?*:*)
    ref="${cur%%:*}"
    cur="${cur#*:}"
    case "$cur" in
    ?*/*)
      pfx="${cur%/*}"
      cur="${cur##*/}"
      ls="$ref:$pfx"
      pfx="$pfx/"
      ;;
    *)
      ls="$ref"
      ;;
      esac

    case "$COMP_WORDBREAKS" in
    *:*) : great ;;
    *)   pfx="$ref:$pfx" ;;
    esac

    local IFS=$'\n'
    COMPREPLY=($(compgen -P "$pfx" \
      -W "$(git --git-dir="$(__gitdir)" ls-tree "$ls" \
        | sed '/^100... blob /{
                   s,^.*  ,,
                   s,$, ,
               }
               /^120000 blob /{
                   s,^.*  ,,
                   s,$, ,
               }
               /^040000 tree /{
                   s,^.*  ,,
                   s,$,/,
               }
               s/^.*  //')" \
      -- "$cur"))
    ;;
  *)
    __gitcomp "$(__git_refs)"
    ;;
  esac
}

__git_complete_revlist ()
{
  local pfx cur="${COMP_WORDS[COMP_CWORD]}"
  case "$cur" in
  *...*)
    pfx="${cur%...*}..."
    cur="${cur#*...}"
    __gitcomp "$(__git_refs)" "$pfx" "$cur"
    ;;
  *..*)
    pfx="${cur%..*}.."
    cur="${cur#*..}"
    __gitcomp "$(__git_refs)" "$pfx" "$cur"
    ;;
  *)
    __gitcomp "$(__git_refs)"
    ;;
  esac
}

__git_complete_remote_or_refspec ()
{
  local cmd="${COMP_WORDS[1]}"
  local cur="${COMP_WORDS[COMP_CWORD]}"
  local i c=2 remote="" pfx="" lhs=1 no_complete_refspec=0
  while [ $c -lt $COMP_CWORD ]; do
    i="${COMP_WORDS[c]}"
    case "$i" in
    --mirror) [ "$cmd" = "push" ] && no_complete_refspec=1 ;;
    --all)
      case "$cmd" in
      push) no_complete_refspec=1 ;;
      fetch)
        COMPREPLY=()
        return
        ;;
      *) ;;
      esac
      ;;
    -*) ;;
    *) remote="$i"; break ;;
    esac
    c=$((++c))
  done
  if [ -z "$remote" ]; then
    __gitcomp "$(__git_remotes)"
    return
  fi
  if [ $no_complete_refspec = 1 ]; then
    COMPREPLY=()
    return
  fi
  [ "$remote" = "." ] && remote=
  case "$cur" in
  *:*)
    case "$COMP_WORDBREAKS" in
    *:*) : great ;;
    *)   pfx="${cur%%:*}:" ;;
    esac
    cur="${cur#*:}"
    lhs=0
    ;;
  +*)
    pfx="+"
    cur="${cur#+}"
    ;;
  esac
  case "$cmd" in
  fetch)
    if [ $lhs = 1 ]; then
      __gitcomp "$(__git_refs2 "$remote")" "$pfx" "$cur"
    else
      __gitcomp "$(__git_refs)" "$pfx" "$cur"
    fi
    ;;
  pull)
    if [ $lhs = 1 ]; then
      __gitcomp "$(__git_refs "$remote")" "$pfx" "$cur"
    else
      __gitcomp "$(__git_refs)" "$pfx" "$cur"
    fi
    ;;
  push)
    if [ $lhs = 1 ]; then
      __gitcomp "$(__git_refs)" "$pfx" "$cur"
    else
      __gitcomp "$(__git_refs "$remote")" "$pfx" "$cur"
    fi
    ;;
  esac
}

__git_complete_strategy ()
{
  __git_compute_merge_strategies
  case "${COMP_WORDS[COMP_CWORD-1]}" in
  -s|--strategy)
    __gitcomp "$__git_merge_strategies"
    return 0
  esac
  local cur="${COMP_WORDS[COMP_CWORD]}"
  case "$cur" in
  --strategy=*)
    __gitcomp "$__git_merge_strategies" "" "${cur##--strategy=}"
    return 0
    ;;
  esac
  return 1
}

__git_list_all_commands ()
{
  local i IFS=" "$'\n'
  for i in $(git help -a|egrep '^  [a-zA-Z0-9]')
  do
    case $i in
    *--*)             : helper pattern;;
    *) echo $i;;
    esac
  done
}

__git_all_commands=
__git_compute_all_commands ()
{
  : ${__git_all_commands:=$(__git_list_all_commands)}
}

__git_list_porcelain_commands ()
{
  local i IFS=" "$'\n'
  __git_compute_all_commands
  for i in "help" $__git_all_commands
  do
    case $i in
    *--*)             : helper pattern;;
    applymbox)        : ask gittus;;
    applypatch)       : ask gittus;;
    archimport)       : import;;
    cat-file)         : plumbing;;
    check-attr)       : plumbing;;
    check-ref-format) : plumbing;;
    checkout-index)   : plumbing;;
    commit-tree)      : plumbing;;
    count-objects)    : infrequent;;
    cvsexportcommit)  : export;;
    cvsimport)        : import;;
    cvsserver)        : daemon;;
    daemon)           : daemon;;
    diff-files)       : plumbing;;
    diff-index)       : plumbing;;
    diff-tree)        : plumbing;;
    fast-import)      : import;;
    fast-export)      : export;;
    fsck-objects)     : plumbing;;
    fetch-pack)       : plumbing;;
    fmt-merge-msg)    : plumbing;;
    for-each-ref)     : plumbing;;
    hash-object)      : plumbing;;
    http-*)           : transport;;
    index-pack)       : plumbing;;
    init-db)          : deprecated;;
    local-fetch)      : plumbing;;
    lost-found)       : infrequent;;
    ls-files)         : plumbing;;
    ls-remote)        : plumbing;;
    ls-tree)          : plumbing;;
    mailinfo)         : plumbing;;
    mailsplit)        : plumbing;;
    merge-*)          : plumbing;;
    mktree)           : plumbing;;
    mktag)            : plumbing;;
    pack-objects)     : plumbing;;
    pack-redundant)   : plumbing;;
    pack-refs)        : plumbing;;
    parse-remote)     : plumbing;;
    patch-id)         : plumbing;;
    peek-remote)      : plumbing;;
    prune)            : plumbing;;
    prune-packed)     : plumbing;;
    quiltimport)      : import;;
    read-tree)        : plumbing;;
    receive-pack)     : plumbing;;
    reflog)           : plumbing;;
    repo-config)      : deprecated;;
    rerere)           : plumbing;;
    rev-list)         : plumbing;;
    rev-parse)        : plumbing;;
    runstatus)        : plumbing;;
    sh-setup)         : internal;;
    shell)            : daemon;;
    show-ref)         : plumbing;;
    send-pack)        : plumbing;;
    show-index)       : plumbing;;
    ssh-*)            : transport;;
    stripspace)       : plumbing;;
    symbolic-ref)     : plumbing;;
    tar-tree)         : deprecated;;
    unpack-file)      : plumbing;;
    unpack-objects)   : plumbing;;
    update-index)     : plumbing;;
    update-ref)       : plumbing;;
    update-server-info) : daemon;;
    upload-archive)   : plumbing;;
    upload-pack)      : plumbing;;
    write-tree)       : plumbing;;
    var)              : infrequent;;
    verify-pack)      : infrequent;;
    verify-tag)       : plumbing;;
    *) echo $i;;
    esac
  done
}

__git_porcelain_commands=
__git_compute_porcelain_commands ()
{
  __git_compute_all_commands
  : ${__git_porcelain_commands:=$(__git_list_porcelain_commands)}
}

__git_aliases ()
{
  local i IFS=$'\n'
  for i in $(git --git-dir="$(__gitdir)" config --get-regexp "alias\..*" 2>/dev/null); do
    case "$i" in
    alias.*)
      i="${i#alias.}"
      echo "${i/ */}"
      ;;
    esac
  done
}

# __git_aliased_command requires 1 argument
__git_aliased_command ()
{
  local word cmdline=$(git --git-dir="$(__gitdir)" \
    config --get "alias.$1")
  for word in $cmdline; do
    if [ "${word##-*}" ]; then
      echo $word
      return
    fi
  done
}

# __git_find_on_cmdline requires 1 argument
__git_find_on_cmdline ()
{
  local word subcommand c=1

  while [ $c -lt $COMP_CWORD ]; do
    word="${COMP_WORDS[c]}"
    for subcommand in $1; do
      if [ "$subcommand" = "$word" ]; then
        echo "$subcommand"
        return
      fi
    done
    c=$((++c))
  done
}

__git_has_doubledash ()
{
  local c=1
  while [ $c -lt $COMP_CWORD ]; do
    if [ "--" = "${COMP_WORDS[c]}" ]; then
      return 0
    fi
    c=$((++c))
  done
  return 1
}

__git_whitespacelist="nowarn warn error error-all fix"

_git_am ()
{
  local cur="${COMP_WORDS[COMP_CWORD]}" dir="$(__gitdir)"
  if [ -d "$dir"/rebase-apply ]; then
    __gitcomp "--skip --resolved --abort"
    return
  fi
  case "$cur" in
  --whitespace=*)
    __gitcomp "$__git_whitespacelist" "" "${cur##--whitespace=}"
    return
    ;;
  --*)
    __gitcomp "
      --3way --committer-date-is-author-date --ignore-date
      --ignore-whitespace --ignore-space-change
      --interactive --keep --no-utf8 --signoff --utf8
      --whitespace= --scissors
      "
    return
  esac
  COMPREPLY=()
}

_git_apply ()
{
  local cur="${COMP_WORDS[COMP_CWORD]}"
  case "$cur" in
  --whitespace=*)
    __gitcomp "$__git_whitespacelist" "" "${cur##--whitespace=}"
    return
    ;;
  --*)
    __gitcomp "
      --stat --numstat --summary --check --index
      --cached --index-info --reverse --reject --unidiff-zero
      --apply --no-add --exclude=
      --ignore-whitespace --ignore-space-change
      --whitespace= --inaccurate-eof --verbose
      "
    return
  esac
  COMPREPLY=()
}

_git_add ()
{
  __git_has_doubledash && return

  local cur="${COMP_WORDS[COMP_CWORD]}"
  case "$cur" in
  --*)
    __gitcomp "
      --interactive --refresh --patch --update --dry-run
      --ignore-errors --intent-to-add
      "
    return
  esac
  COMPREPLY=()
}

_git_archive ()
{
  local cur="${COMP_WORDS[COMP_CWORD]}"
  case "$cur" in
  --format=*)
    __gitcomp "$(git archive --list)" "" "${cur##--format=}"
    return
    ;;
  --remote=*)
    __gitcomp "$(__git_remotes)" "" "${cur##--remote=}"
    return
    ;;
  --*)
    __gitcomp "
      --format= --list --verbose
      --prefix= --remote= --exec=
      "
    return
    ;;
  esac
  __git_complete_file
}

_git_bisect ()
{
  __git_has_doubledash && return

  local subcommands="start bad good skip reset visualize replay log run"
  local subcommand="$(__git_find_on_cmdline "$subcommands")"
  if [ -z "$subcommand" ]; then
    __gitcomp "$subcommands"
    return
  fi

  case "$subcommand" in
  bad|good|reset|skip)
    __gitcomp "$(__git_refs)"
    ;;
  *)
    COMPREPLY=()
    ;;
  esac
}

_git_branch ()
{
  local i c=1 only_local_ref="n" has_r="n"

  while [ $c -lt $COMP_CWORD ]; do
    i="${COMP_WORDS[c]}"
    case "$i" in
    -d|-m)  only_local_ref="y" ;;
    -r) has_r="y" ;;
    esac
    c=$((++c))
  done

  case "${COMP_WORDS[COMP_CWORD]}" in
  --*)
    __gitcomp "
      --color --no-color --verbose --abbrev= --no-abbrev
      --track --no-track --contains --merged --no-merged
      "
    ;;
  *)
    if [ $only_local_ref = "y" -a $has_r = "n" ]; then
      __gitcomp "$(__git_heads)"
    else
      __gitcomp "$(__git_refs)"
    fi
    ;;
  esac
}

_git_bundle ()
{
  local cmd="${COMP_WORDS[2]}"
  case "$COMP_CWORD" in
  2)
    __gitcomp "create list-heads verify unbundle"
    ;;
  3)
    # looking for a file
    ;;
  *)
    case "$cmd" in
      create)
        __git_complete_revlist
      ;;
    esac
    ;;
  esac
}

_git_checkout ()
{
  __git_has_doubledash && return

  local cur="${COMP_WORDS[COMP_CWORD]}"
  case "$cur" in
  --conflict=*)
    __gitcomp "diff3 merge" "" "${cur##--conflict=}"
    ;;
  --*)
    __gitcomp "
      --quiet --ours --theirs --track --no-track --merge
      --conflict= --patch
      "
    ;;
  *)
    __gitcomp "$(__git_refs)"
    ;;
  esac
}

_git_cherry ()
{
  __gitcomp "$(__git_refs)"
}

_git_cherry_pick ()
{
  local cur="${COMP_WORDS[COMP_CWORD]}"
  case "$cur" in
  --*)
    __gitcomp "--edit --no-commit"
    ;;
  *)
    __gitcomp "$(__git_refs)"
    ;;
  esac
}

_git_clean ()
{
  __git_has_doubledash && return

  local cur="${COMP_WORDS[COMP_CWORD]}"
  case "$cur" in
  --*)
    __gitcomp "--dry-run --quiet"
    return
    ;;
  esac
  COMPREPLY=()
}

_git_clone ()
{
  local cur="${COMP_WORDS[COMP_CWORD]}"
  case "$cur" in
  --*)
    __gitcomp "
      --local
      --no-hardlinks
      --shared
      --reference
      --quiet
      --no-checkout
      --bare
      --mirror
      --origin
      --upload-pack
      --template=
      --depth
      "
    return
    ;;
  esac
  COMPREPLY=()
}

_git_commit ()
{
  __git_has_doubledash && return

  local cur="${COMP_WORDS[COMP_CWORD]}"
  case "$cur" in
  --cleanup=*)
    __gitcomp "default strip verbatim whitespace
      " "" "${cur##--cleanup=}"
    return
    ;;
  --reuse-message=*)
    __gitcomp "$(__git_refs)" "" "${cur##--reuse-message=}"
    return
    ;;
  --reedit-message=*)
    __gitcomp "$(__git_refs)" "" "${cur##--reedit-message=}"
    return
    ;;
  --untracked-files=*)
    __gitcomp "all no normal" "" "${cur##--untracked-files=}"
    return
    ;;
  --*)
    __gitcomp "
      --all --author= --signoff --verify --no-verify
      --edit --amend --include --only --interactive
      --dry-run --reuse-message= --reedit-message=
      --reset-author --file= --message= --template=
      --cleanup= --untracked-files --untracked-files=
      --verbose --quiet
      "
    return
  esac
  COMPREPLY=()
}

_git_describe ()
{
  local cur="${COMP_WORDS[COMP_CWORD]}"
  case "$cur" in
  --*)
    __gitcomp "
      --all --tags --contains --abbrev= --candidates=
      --exact-match --debug --long --match --always
      "
    return
  esac
  __gitcomp "$(__git_refs)"
}

__git_diff_common_options="--stat --numstat --shortstat --summary
      --patch-with-stat --name-only --name-status --color
      --no-color --color-words --no-renames --check
      --full-index --binary --abbrev --diff-filter=
      --find-copies-harder
      --text --ignore-space-at-eol --ignore-space-change
      --ignore-all-space --exit-code --quiet --ext-diff
      --no-ext-diff
      --no-prefix --src-prefix= --dst-prefix=
      --inter-hunk-context=
      --patience
      --raw
      --dirstat --dirstat= --dirstat-by-file
      --dirstat-by-file= --cumulative
"

_git_diff ()
{
  __git_has_doubledash && return

  local cur="${COMP_WORDS[COMP_CWORD]}"
  case "$cur" in
  --*)
    __gitcomp "--cached --staged --pickaxe-all --pickaxe-regex
      --base --ours --theirs
      $__git_diff_common_options
      "
    return
    ;;
  esac
  __git_complete_file
}

__git_mergetools_common="diffuse ecmerge emerge kdiff3 meld opendiff
      tkdiff vimdiff gvimdiff xxdiff araxis p4merge
"

_git_difftool ()
{
  __git_has_doubledash && return

  local cur="${COMP_WORDS[COMP_CWORD]}"
  case "$cur" in
  --tool=*)
    __gitcomp "$__git_mergetools_common kompare" "" "${cur##--tool=}"
    return
    ;;
  --*)
    __gitcomp "--cached --staged --pickaxe-all --pickaxe-regex
      --base --ours --theirs
      --no-renames --diff-filter= --find-copies-harder
      --relative --ignore-submodules
      --tool="
    return
    ;;
  esac
  __git_complete_file
}

__git_fetch_options="
  --quiet --verbose --append --upload-pack --force --keep --depth=
  --tags --no-tags --all --prune --dry-run
"

_git_fetch ()
{
  local cur="${COMP_WORDS[COMP_CWORD]}"
  case "$cur" in
  --*)
    __gitcomp "$__git_fetch_options"
    return
    ;;
  esac
  __git_complete_remote_or_refspec
}

_git_format_patch ()
{
  local cur="${COMP_WORDS[COMP_CWORD]}"
  case "$cur" in
  --thread=*)
    __gitcomp "
      deep shallow
      " "" "${cur##--thread=}"
    return
    ;;
  --*)
    __gitcomp "
      --stdout --attach --no-attach --thread --thread=
      --output-directory
      --numbered --start-number
      --numbered-files
      --keep-subject
      --signoff
      --in-reply-to= --cc=
      --full-index --binary
      --not --all
      --cover-letter
      --no-prefix --src-prefix= --dst-prefix=
      --inline --suffix= --ignore-if-in-upstream
      --subject-prefix=
      "
    return
    ;;
  esac
  __git_complete_revlist
}

_git_fsck ()
{
  local cur="${COMP_WORDS[COMP_CWORD]}"
  case "$cur" in
  --*)
    __gitcomp "
      --tags --root --unreachable --cache --no-reflogs --full
      --strict --verbose --lost-found
      "
    return
    ;;
  esac
  COMPREPLY=()
}

_git_gc ()
{
  local cur="${COMP_WORDS[COMP_CWORD]}"
  case "$cur" in
  --*)
    __gitcomp "--prune --aggressive"
    return
    ;;
  esac
  COMPREPLY=()
}

_git_grep ()
{
  __git_has_doubledash && return

  local cur="${COMP_WORDS[COMP_CWORD]}"
  case "$cur" in
  --*)
    __gitcomp "
      --cached
      --text --ignore-case --word-regexp --invert-match
      --full-name
      --extended-regexp --basic-regexp --fixed-strings
      --files-with-matches --name-only
      --files-without-match
      --max-depth
      --count
      --and --or --not --all-match
      "
    return
    ;;
  esac

  __gitcomp "$(__git_refs)"
}

_git_help ()
{
  local cur="${COMP_WORDS[COMP_CWORD]}"
  case "$cur" in
  --*)
    __gitcomp "--all --info --man --web"
    return
    ;;
  esac
  __git_compute_all_commands
  __gitcomp "$__git_all_commands
    attributes cli core-tutorial cvs-migration
    diffcore gitk glossary hooks ignore modules
    repository-layout tutorial tutorial-2
    workflows
    "
}

_git_init ()
{
  local cur="${COMP_WORDS[COMP_CWORD]}"
  case "$cur" in
  --shared=*)
    __gitcomp "
      false true umask group all world everybody
      " "" "${cur##--shared=}"
    return
    ;;
  --*)
    __gitcomp "--quiet --bare --template= --shared --shared="
    return
    ;;
  esac
  COMPREPLY=()
}

_git_ls_files ()
{
  __git_has_doubledash && return

  local cur="${COMP_WORDS[COMP_CWORD]}"
  case "$cur" in
  --*)
    __gitcomp "--cached --deleted --modified --others --ignored
      --stage --directory --no-empty-directory --unmerged
      --killed --exclude= --exclude-from=
      --exclude-per-directory= --exclude-standard
      --error-unmatch --with-tree= --full-name
      --abbrev --ignored --exclude-per-directory
      "
    return
    ;;
  esac
  COMPREPLY=()
}

_git_ls_remote ()
{
  __gitcomp "$(__git_remotes)"
}

_git_ls_tree ()
{
  __git_complete_file
}

# Options that go well for log, shortlog and gitk
__git_log_common_options="
  --not --all
  --branches --tags --remotes
  --first-parent --merges --no-merges
  --max-count=
  --max-age= --since= --after=
  --min-age= --until= --before=
"
# Options that go well for log and gitk (not shortlog)
__git_log_gitk_options="
  --dense --sparse --full-history
  --simplify-merges --simplify-by-decoration
  --left-right
"
# Options that go well for log and shortlog (not gitk)
__git_log_shortlog_options="
  --author= --committer= --grep=
  --all-match
"

__git_log_pretty_formats="oneline short medium full fuller email raw format:"
__git_log_date_formats="relative iso8601 rfc2822 short local default raw"

_git_log ()
{
  __git_has_doubledash && return

  local cur="${COMP_WORDS[COMP_CWORD]}"
  local g="$(git rev-parse --git-dir 2>/dev/null)"
  local merge=""
  if [ -f "$g/MERGE_HEAD" ]; then
    merge="--merge"
  fi
  case "$cur" in
  --pretty=*)
    __gitcomp "$__git_log_pretty_formats
      " "" "${cur##--pretty=}"
    return
    ;;
  --format=*)
    __gitcomp "$__git_log_pretty_formats
      " "" "${cur##--format=}"
    return
    ;;
  --date=*)
    __gitcomp "$__git_log_date_formats" "" "${cur##--date=}"
    return
    ;;
  --decorate=*)
    __gitcomp "long short" "" "${cur##--decorate=}"
    return
    ;;
  --*)
    __gitcomp "
      $__git_log_common_options
      $__git_log_shortlog_options
      $__git_log_gitk_options
      --root --topo-order --date-order --reverse
      --follow --full-diff
      --abbrev-commit --abbrev=
      --relative-date --date=
      --pretty= --format= --oneline
      --cherry-pick
      --graph
      --decorate --decorate=
      --walk-reflogs
      --parents --children
      $merge
      $__git_diff_common_options
      --pickaxe-all --pickaxe-regex
      "
    return
    ;;
  esac
  __git_complete_revlist
}

__git_merge_options="
  --no-commit --no-stat --log --no-log --squash --strategy
  --commit --stat --no-squash --ff --no-ff --ff-only
"

_git_merge ()
{
  __git_complete_strategy && return

  local cur="${COMP_WORDS[COMP_CWORD]}"
  case "$cur" in
  --*)
    __gitcomp "$__git_merge_options"
    return
  esac
  __gitcomp "$(__git_refs)"
}

_git_mergetool ()
{
  local cur="${COMP_WORDS[COMP_CWORD]}"
  case "$cur" in
  --tool=*)
    __gitcomp "$__git_mergetools_common tortoisemerge" "" "${cur##--tool=}"
    return
    ;;
  --*)
    __gitcomp "--tool="
    return
    ;;
  esac
  COMPREPLY=()
}

_git_merge_base ()
{
  __gitcomp "$(__git_refs)"
}

_git_mv ()
{
  local cur="${COMP_WORDS[COMP_CWORD]}"
  case "$cur" in
  --*)
    __gitcomp "--dry-run"
    return
    ;;
  esac
  COMPREPLY=()
}

_git_name_rev ()
{
  __gitcomp "--tags --all --stdin"
}

_git_pull ()
{
  __git_complete_strategy && return

  local cur="${COMP_WORDS[COMP_CWORD]}"
  case "$cur" in
  --*)
    __gitcomp "
      --rebase --no-rebase
      $__git_merge_options
      $__git_fetch_options
    "
    return
    ;;
  esac
  __git_complete_remote_or_refspec
}

_git_push ()
{
  local cur="${COMP_WORDS[COMP_CWORD]}"
  case "${COMP_WORDS[COMP_CWORD-1]}" in
  --repo)
    __gitcomp "$(__git_remotes)"
    return
  esac
  case "$cur" in
  --repo=*)
    __gitcomp "$(__git_remotes)" "" "${cur##--repo=}"
    return
    ;;
  --*)
    __gitcomp "
      --all --mirror --tags --dry-run --force --verbose
      --receive-pack= --repo=
    "
    return
    ;;
  esac
  __git_complete_remote_or_refspec
}

_git_rebase ()
{
  local cur="${COMP_WORDS[COMP_CWORD]}" dir="$(__gitdir)"
  if [ -d "$dir"/rebase-apply ] || [ -d "$dir"/rebase-merge ]; then
    __gitcomp "--continue --skip --abort"
    return
  fi
  __git_complete_strategy && return
  case "$cur" in
  --whitespace=*)
    __gitcomp "$__git_whitespacelist" "" "${cur##--whitespace=}"
    return
    ;;
  --*)
    __gitcomp "
      --onto --merge --strategy --interactive
      --preserve-merges --stat --no-stat
      --committer-date-is-author-date --ignore-date
      --ignore-whitespace --whitespace=
      "

    return
  esac
  __gitcomp "$(__git_refs)"
}

__git_send_email_confirm_options="always never auto cc compose"
__git_send_email_suppresscc_options="author self cc bodycc sob cccmd body all"

_git_send_email ()
{
  local cur="${COMP_WORDS[COMP_CWORD]}"
  case "$cur" in
  --confirm=*)
    __gitcomp "
      $__git_send_email_confirm_options
      " "" "${cur##--confirm=}"
    return
    ;;
  --suppress-cc=*)
    __gitcomp "
      $__git_send_email_suppresscc_options
      " "" "${cur##--suppress-cc=}"

    return
    ;;
  --smtp-encryption=*)
    __gitcomp "ssl tls" "" "${cur##--smtp-encryption=}"
    return
    ;;
  --*)
    __gitcomp "--annotate --bcc --cc --cc-cmd --chain-reply-to
      --compose --confirm= --dry-run --envelope-sender
      --from --identity
      --in-reply-to --no-chain-reply-to --no-signed-off-by-cc
      --no-suppress-from --no-thread --quiet
      --signed-off-by-cc --smtp-pass --smtp-server
      --smtp-server-port --smtp-encryption= --smtp-user
      --subject --suppress-cc= --suppress-from --thread --to
      --validate --no-validate"
    return
    ;;
  esac
  COMPREPLY=()
}

__git_config_get_set_variables ()
{
  local prevword word config_file= c=$COMP_CWORD
  while [ $c -gt 1 ]; do
    word="${COMP_WORDS[c]}"
    case "$word" in
    --global|--system|--file=*)
      config_file="$word"
      break
      ;;
    -f|--file)
      config_file="$word $prevword"
      break
      ;;
    esac
    prevword=$word
    c=$((--c))
  done

  git --git-dir="$(__gitdir)" config $config_file --list 2>/dev/null |
  while read line
  do
    case "$line" in
    *.*=*)
      echo "${line/=*/}"
      ;;
    esac
  done
}

_git_config ()
{
  local cur="${COMP_WORDS[COMP_CWORD]}"
  local prv="${COMP_WORDS[COMP_CWORD-1]}"
  case "$prv" in
  branch.*.remote)
    __gitcomp "$(__git_remotes)"
    return
    ;;
  branch.*.merge)
    __gitcomp "$(__git_refs)"
    return
    ;;
  remote.*.fetch)
    local remote="${prv#remote.}"
    remote="${remote%.fetch}"
    __gitcomp "$(__git_refs_remotes "$remote")"
    return
    ;;
  remote.*.push)
    local remote="${prv#remote.}"
    remote="${remote%.push}"
    __gitcomp "$(git --git-dir="$(__gitdir)" \
      for-each-ref --format='%(refname):%(refname)' \
      refs/heads)"
    return
    ;;
  pull.twohead|pull.octopus)
    __git_compute_merge_strategies
    __gitcomp "$__git_merge_strategies"
    return
    ;;
  color.branch|color.diff|color.interactive|\
  color.showbranch|color.status|color.ui)
    __gitcomp "always never auto"
    return
    ;;
  color.pager)
    __gitcomp "false true"
    return
    ;;
  color.*.*)
    __gitcomp "
      normal black red green yellow blue magenta cyan white
      bold dim ul blink reverse
      "
    return
    ;;
  help.format)
    __gitcomp "man info web html"
    return
    ;;
  log.date)
    __gitcomp "$__git_log_date_formats"
    return
    ;;
  sendemail.aliasesfiletype)
    __gitcomp "mutt mailrc pine elm gnus"
    return
    ;;
  sendemail.confirm)
    __gitcomp "$__git_send_email_confirm_options"
    return
    ;;
  sendemail.suppresscc)
    __gitcomp "$__git_send_email_suppresscc_options"
    return
    ;;
  --get|--get-all|--unset|--unset-all)
    __gitcomp "$(__git_config_get_set_variables)"
    return
    ;;
  *.*)
    COMPREPLY=()
    return
    ;;
  esac
  case "$cur" in
  --*)
    __gitcomp "
      --global --system --file=
      --list --replace-all
      --get --get-all --get-regexp
      --add --unset --unset-all
      --remove-section --rename-section
      "
    return
    ;;
  branch.*.*)
    local pfx="${cur%.*}."
    cur="${cur##*.}"
    __gitcomp "remote merge mergeoptions rebase" "$pfx" "$cur"
    return
    ;;
  branch.*)
    local pfx="${cur%.*}."
    cur="${cur#*.}"
    __gitcomp "$(__git_heads)" "$pfx" "$cur" "."
    return
    ;;
  guitool.*.*)
    local pfx="${cur%.*}."
    cur="${cur##*.}"
    __gitcomp "
      argprompt cmd confirm needsfile noconsole norescan
      prompt revprompt revunmerged title
      " "$pfx" "$cur"
    return
    ;;
  difftool.*.*)
    local pfx="${cur%.*}."
    cur="${cur##*.}"
    __gitcomp "cmd path" "$pfx" "$cur"
    return
    ;;
  man.*.*)
    local pfx="${cur%.*}."
    cur="${cur##*.}"
    __gitcomp "cmd path" "$pfx" "$cur"
    return
    ;;
  mergetool.*.*)
    local pfx="${cur%.*}."
    cur="${cur##*.}"
    __gitcomp "cmd path trustExitCode" "$pfx" "$cur"
    return
    ;;
  pager.*)
    local pfx="${cur%.*}."
    cur="${cur#*.}"
    __git_compute_all_commands
    __gitcomp "$__git_all_commands" "$pfx" "$cur"
    return
    ;;
  remote.*.*)
    local pfx="${cur%.*}."
    cur="${cur##*.}"
    __gitcomp "
      url proxy fetch push mirror skipDefaultUpdate
      receivepack uploadpack tagopt pushurl
      " "$pfx" "$cur"
    return
    ;;
  remote.*)
    local pfx="${cur%.*}."
    cur="${cur#*.}"
    __gitcomp "$(__git_remotes)" "$pfx" "$cur" "."
    return
    ;;
  url.*.*)
    local pfx="${cur%.*}."
    cur="${cur##*.}"
    __gitcomp "insteadOf pushInsteadOf" "$pfx" "$cur"
    return
    ;;
  esac
  __gitcomp "
    add.ignore-errors
    alias.
    apply.ignorewhitespace
    apply.whitespace
    branch.autosetupmerge
    branch.autosetuprebase
    clean.requireForce
    color.branch
    color.branch.current
    color.branch.local
    color.branch.plain
    color.branch.remote
    color.diff
    color.diff.commit
    color.diff.frag
    color.diff.meta
    color.diff.new
    color.diff.old
    color.diff.plain
    color.diff.whitespace
    color.grep
    color.grep.external
    color.grep.match
    color.interactive
    color.interactive.header
    color.interactive.help
    color.interactive.prompt
    color.pager
    color.showbranch
    color.status
    color.status.added
    color.status.changed
    color.status.header
    color.status.nobranch
    color.status.untracked
    color.status.updated
    color.ui
    commit.template
    core.autocrlf
    core.bare
    core.compression
    core.createObject
    core.deltaBaseCacheLimit
    core.editor
    core.excludesfile
    core.fileMode
    core.fsyncobjectfiles
    core.gitProxy
    core.ignoreCygwinFSTricks
    core.ignoreStat
    core.logAllRefUpdates
    core.loosecompression
    core.packedGitLimit
    core.packedGitWindowSize
    core.pager
    core.preferSymlinkRefs
    core.preloadindex
    core.quotepath
    core.repositoryFormatVersion
    core.safecrlf
    core.sharedRepository
    core.symlinks
    core.trustctime
    core.warnAmbiguousRefs
    core.whitespace
    core.worktree
    diff.autorefreshindex
    diff.external
    diff.mnemonicprefix
    diff.renameLimit
    diff.renameLimit.
    diff.renames
    diff.suppressBlankEmpty
    diff.tool
    diff.wordRegex
    difftool.
    difftool.prompt
    fetch.unpackLimit
    format.attach
    format.cc
    format.headers
    format.numbered
    format.pretty
    format.signoff
    format.subjectprefix
    format.suffix
    format.thread
    gc.aggressiveWindow
    gc.auto
    gc.autopacklimit
    gc.packrefs
    gc.pruneexpire
    gc.reflogexpire
    gc.reflogexpireunreachable
    gc.rerereresolved
    gc.rerereunresolved
    gitcvs.allbinary
    gitcvs.commitmsgannotation
    gitcvs.dbTableNamePrefix
    gitcvs.dbdriver
    gitcvs.dbname
    gitcvs.dbpass
    gitcvs.dbuser
    gitcvs.enabled
    gitcvs.logfile
    gitcvs.usecrlfattr
    guitool.
    gui.blamehistoryctx
    gui.commitmsgwidth
    gui.copyblamethreshold
    gui.diffcontext
    gui.encoding
    gui.fastcopyblame
    gui.matchtrackingbranch
    gui.newbranchtemplate
    gui.pruneduringfetch
    gui.spellingdictionary
    gui.trustmtime
    help.autocorrect
    help.browser
    help.format
    http.lowSpeedLimit
    http.lowSpeedTime
    http.maxRequests
    http.noEPSV
    http.proxy
    http.sslCAInfo
    http.sslCAPath
    http.sslCert
    http.sslKey
    http.sslVerify
    i18n.commitEncoding
    i18n.logOutputEncoding
    imap.folder
    imap.host
    imap.pass
    imap.port
    imap.preformattedHTML
    imap.sslverify
    imap.tunnel
    imap.user
    instaweb.browser
    instaweb.httpd
    instaweb.local
    instaweb.modulepath
    instaweb.port
    interactive.singlekey
    log.date
    log.showroot
    mailmap.file
    man.
    man.viewer
    merge.conflictstyle
    merge.log
    merge.renameLimit
    merge.stat
    merge.tool
    merge.verbosity
    mergetool.
    mergetool.keepBackup
    mergetool.prompt
    pack.compression
    pack.deltaCacheLimit
    pack.deltaCacheSize
    pack.depth
    pack.indexVersion
    pack.packSizeLimit
    pack.threads
    pack.window
    pack.windowMemory
    pager.
    pull.octopus
    pull.twohead
    push.default
    rebase.stat
    receive.denyCurrentBranch
    receive.denyDeletes
    receive.denyNonFastForwards
    receive.fsckObjects
    receive.unpackLimit
    repack.usedeltabaseoffset
    rerere.autoupdate
    rerere.enabled
    sendemail.aliasesfile
    sendemail.aliasesfiletype
    sendemail.bcc
    sendemail.cc
    sendemail.cccmd
    sendemail.chainreplyto
    sendemail.confirm
    sendemail.envelopesender
    sendemail.multiedit
    sendemail.signedoffbycc
    sendemail.smtpencryption
    sendemail.smtppass
    sendemail.smtpserver
    sendemail.smtpserverport
    sendemail.smtpuser
    sendemail.suppresscc
    sendemail.suppressfrom
    sendemail.thread
    sendemail.to
    sendemail.validate
    showbranch.default
    status.relativePaths
    status.showUntrackedFiles
    tar.umask
    transfer.unpackLimit
    url.
    user.email
    user.name
    user.signingkey
    web.browser
    branch. remote.
  "
}

_git_remote ()
{
  local subcommands="add rename rm show prune update set-head"
  local subcommand="$(__git_find_on_cmdline "$subcommands")"
  if [ -z "$subcommand" ]; then
    __gitcomp "$subcommands"
    return
  fi

  case "$subcommand" in
  rename|rm|show|prune)
    __gitcomp "$(__git_remotes)"
    ;;
  update)
    local i c='' IFS=$'\n'
    for i in $(git --git-dir="$(__gitdir)" config --get-regexp "remotes\..*" 2>/dev/null); do
      i="${i#remotes.}"
      c="$c ${i/ */}"
    done
    __gitcomp "$c"
    ;;
  *)
    COMPREPLY=()
    ;;
  esac
}

_git_replace ()
{
  __gitcomp "$(__git_refs)"
}

_git_reset ()
{
  __git_has_doubledash && return

  local cur="${COMP_WORDS[COMP_CWORD]}"
  case "$cur" in
  --*)
    __gitcomp "--merge --mixed --hard --soft --patch"
    return
    ;;
  esac
  __gitcomp "$(__git_refs)"
}

_git_revert ()
{
  local cur="${COMP_WORDS[COMP_CWORD]}"
  case "$cur" in
  --*)
    __gitcomp "--edit --mainline --no-edit --no-commit --signoff"
    return
    ;;
  esac
  __gitcomp "$(__git_refs)"
}

_git_rm ()
{
  __git_has_doubledash && return

  local cur="${COMP_WORDS[COMP_CWORD]}"
  case "$cur" in
  --*)
    __gitcomp "--cached --dry-run --ignore-unmatch --quiet"
    return
    ;;
  esac
  COMPREPLY=()
}

_git_shortlog ()
{
  __git_has_doubledash && return

  local cur="${COMP_WORDS[COMP_CWORD]}"
  case "$cur" in
  --*)
    __gitcomp "
      $__git_log_common_options
      $__git_log_shortlog_options
      --numbered --summary
      "
    return
    ;;
  esac
  __git_complete_revlist
}

_git_show ()
{
  __git_has_doubledash && return

  local cur="${COMP_WORDS[COMP_CWORD]}"
  case "$cur" in
  --pretty=*)
    __gitcomp "$__git_log_pretty_formats
      " "" "${cur##--pretty=}"
    return
    ;;
  --format=*)
    __gitcomp "$__git_log_pretty_formats
      " "" "${cur##--format=}"
    return
    ;;
  --*)
    __gitcomp "--pretty= --format= --abbrev-commit --oneline
      $__git_diff_common_options
      "
    return
    ;;
  esac
  __git_complete_file
}

_git_show_branch ()
{
  local cur="${COMP_WORDS[COMP_CWORD]}"
  case "$cur" in
  --*)
    __gitcomp "
      --all --remotes --topo-order --current --more=
      --list --independent --merge-base --no-name
      --color --no-color
      --sha1-name --sparse --topics --reflog
      "
    return
    ;;
  esac
  __git_complete_revlist
}

_git_stash ()
{
  local cur="${COMP_WORDS[COMP_CWORD]}"
  local save_opts='--keep-index --no-keep-index --quiet --patch'
  local subcommands='save list show apply clear drop pop create branch'
  local subcommand="$(__git_find_on_cmdline "$subcommands")"
  if [ -z "$subcommand" ]; then
    case "$cur" in
    --*)
      __gitcomp "$save_opts"
      ;;
    *)
      if [ -z "$(__git_find_on_cmdline "$save_opts")" ]; then
        __gitcomp "$subcommands"
      else
        COMPREPLY=()
      fi
      ;;
    esac
  else
    case "$subcommand,$cur" in
    save,--*)
      __gitcomp "$save_opts"
      ;;
    apply,--*|pop,--*)
      __gitcomp "--index --quiet"
      ;;
    show,--*|drop,--*|branch,--*)
      COMPREPLY=()
      ;;
    show,*|apply,*|drop,*|pop,*|branch,*)
      __gitcomp "$(git --git-dir="$(__gitdir)" stash list \
          | sed -n -e 's/:.*//p')"
      ;;
    *)
      COMPREPLY=()
      ;;
    esac
  fi
}

_git_submodule ()
{
  __git_has_doubledash && return

  local subcommands="add status init update summary foreach sync"
  if [ -z "$(__git_find_on_cmdline "$subcommands")" ]; then
    local cur="${COMP_WORDS[COMP_CWORD]}"
    case "$cur" in
    --*)
      __gitcomp "--quiet --cached"
      ;;
    *)
      __gitcomp "$subcommands"
      ;;
    esac
    return
  fi
}

_git_svn ()
{
  local subcommands="
    init fetch clone rebase dcommit log find-rev
    set-tree commit-diff info create-ignore propget
    proplist show-ignore show-externals branch tag blame
    migrate mkdirs reset gc
    "
  local subcommand="$(__git_find_on_cmdline "$subcommands")"
  if [ -z "$subcommand" ]; then
    __gitcomp "$subcommands"
  else
    local remote_opts="--username= --config-dir= --no-auth-cache"
    local fc_opts="
      --follow-parent --authors-file= --repack=
      --no-metadata --use-svm-props --use-svnsync-props
      --log-window-size= --no-checkout --quiet
      --repack-flags --use-log-author --localtime
      --ignore-paths= $remote_opts
      "
    local init_opts="
      --template= --shared= --trunk= --tags=
      --branches= --stdlayout --minimize-url
      --no-metadata --use-svm-props --use-svnsync-props
      --rewrite-root= --prefix= --use-log-author
      --add-author-from $remote_opts
      "
    local cmt_opts="
      --edit --rmdir --find-copies-harder --copy-similarity=
      "

    local cur="${COMP_WORDS[COMP_CWORD]}"
    case "$subcommand,$cur" in
    fetch,--*)
      __gitcomp "--revision= --fetch-all $fc_opts"
      ;;
    clone,--*)
      __gitcomp "--revision= $fc_opts $init_opts"
      ;;
    init,--*)
      __gitcomp "$init_opts"
      ;;
    dcommit,--*)
      __gitcomp "
        --merge --strategy= --verbose --dry-run
        --fetch-all --no-rebase --commit-url
        --revision $cmt_opts $fc_opts
        "
      ;;
    set-tree,--*)
      __gitcomp "--stdin $cmt_opts $fc_opts"
      ;;
    create-ignore,--*|propget,--*|proplist,--*|show-ignore,--*|\
    show-externals,--*|mkdirs,--*)
      __gitcomp "--revision="
      ;;
    log,--*)
      __gitcomp "
        --limit= --revision= --verbose --incremental
        --oneline --show-commit --non-recursive
        --authors-file= --color
        "
      ;;
    rebase,--*)
      __gitcomp "
        --merge --verbose --strategy= --local
        --fetch-all --dry-run $fc_opts
        "
      ;;
    commit-diff,--*)
      __gitcomp "--message= --file= --revision= $cmt_opts"
      ;;
    info,--*)
      __gitcomp "--url"
      ;;
    branch,--*)
      __gitcomp "--dry-run --message --tag"
      ;;
    tag,--*)
      __gitcomp "--dry-run --message"
      ;;
    blame,--*)
      __gitcomp "--git-format"
      ;;
    migrate,--*)
      __gitcomp "
        --config-dir= --ignore-paths= --minimize
        --no-auth-cache --username=
        "
      ;;
    reset,--*)
      __gitcomp "--revision= --parent"
      ;;
    *)
      COMPREPLY=()
      ;;
    esac
  fi
}

_git_tag ()
{
  local i c=1 f=0
  while [ $c -lt $COMP_CWORD ]; do
    i="${COMP_WORDS[c]}"
    case "$i" in
    -d|-v)
      __gitcomp "$(__git_tags)"
      return
      ;;
    -f)
      f=1
      ;;
    esac
    c=$((++c))
  done

  case "${COMP_WORDS[COMP_CWORD-1]}" in
  -m|-F)
    COMPREPLY=()
    ;;
  -*|tag)
    if [ $f = 1 ]; then
      __gitcomp "$(__git_tags)"
    else
      COMPREPLY=()
    fi
    ;;
  *)
    __gitcomp "$(__git_refs)"
    ;;
  esac
}

_git ()
{
  local i c=1 command __git_dir

  while [ $c -lt $COMP_CWORD ]; do
    i="${COMP_WORDS[c]}"
    case "$i" in
    --git-dir=*) __git_dir="${i#--git-dir=}" ;;
    --bare)      __git_dir="." ;;
    --version|-p|--paginate) ;;
    --help) command="help"; break ;;
    *) command="$i"; break ;;
    esac
    c=$((++c))
  done

  if [ -z "$command" ]; then
    case "${COMP_WORDS[COMP_CWORD]}" in
    --*)   __gitcomp "
      --paginate
      --no-pager
      --git-dir=
      --bare
      --version
      --exec-path
      --html-path
      --work-tree=
      --help
      "
      ;;
    *)     __git_compute_porcelain_commands
           __gitcomp "$__git_porcelain_commands $(__git_aliases)" ;;
    esac
    return
  fi

  local expansion=$(__git_aliased_command "$command")
  [ "$expansion" ] && command="$expansion"

  case "$command" in
  am)          _git_am ;;
  add)         _git_add ;;
  apply)       _git_apply ;;
  archive)     _git_archive ;;
  bisect)      _git_bisect ;;
  bundle)      _git_bundle ;;
  branch)      _git_branch ;;
  checkout)    _git_checkout ;;
  cherry)      _git_cherry ;;
  cherry-pick) _git_cherry_pick ;;
  clean)       _git_clean ;;
  clone)       _git_clone ;;
  commit)      _git_commit ;;
  config)      _git_config ;;
  describe)    _git_describe ;;
  diff)        _git_diff ;;
  difftool)    _git_difftool ;;
  fetch)       _git_fetch ;;
  format-patch) _git_format_patch ;;
  fsck)        _git_fsck ;;
  gc)          _git_gc ;;
  grep)        _git_grep ;;
  help)        _git_help ;;
  init)        _git_init ;;
  log)         _git_log ;;
  ls-files)    _git_ls_files ;;
  ls-remote)   _git_ls_remote ;;
  ls-tree)     _git_ls_tree ;;
  merge)       _git_merge;;
  mergetool)   _git_mergetool;;
  merge-base)  _git_merge_base ;;
  mv)          _git_mv ;;
  name-rev)    _git_name_rev ;;
  pull)        _git_pull ;;
  push)        _git_push ;;
  rebase)      _git_rebase ;;
  remote)      _git_remote ;;
  replace)     _git_replace ;;
  reset)       _git_reset ;;
  revert)      _git_revert ;;
  rm)          _git_rm ;;
  send-email)  _git_send_email ;;
  shortlog)    _git_shortlog ;;
  show)        _git_show ;;
  show-branch) _git_show_branch ;;
  stash)       _git_stash ;;
  stage)       _git_add ;;
  submodule)   _git_submodule ;;
  svn)         _git_svn ;;
  tag)         _git_tag ;;
  whatchanged) _git_log ;;
  *)           COMPREPLY=() ;;
  esac
}

_gitk ()
{
  __git_has_doubledash && return

  local cur="${COMP_WORDS[COMP_CWORD]}"
  local g="$(__gitdir)"
  local merge=""
  if [ -f "$g/MERGE_HEAD" ]; then
    merge="--merge"
  fi
  case "$cur" in
  --*)
    __gitcomp "
      $__git_log_common_options
      $__git_log_gitk_options
      $merge
      "
    return
    ;;
  esac
  __git_complete_revlist
}

complete -o bashdefault -o default -o nospace -F _git git 2>/dev/null \
  || complete -o default -o nospace -F _git git
complete -o bashdefault -o default -o nospace -F _gitk gitk 2>/dev/null \
  || complete -o default -o nospace -F _gitk gitk

# The following are necessary only for Cygwin, and only are needed
# when the user has tab-completed the executable name and consequently
# included the '.exe' suffix.
#
if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then
complete -o bashdefault -o default -o nospace -F _git git.exe 2>/dev/null \
  || complete -o default -o nospace -F _git git.exe
fi
