GIT 自动补全命令,分支名 以及 高亮显示当前分支

GIT 自动补全命令,分支名 以及 高亮显示当前分支 配置

  1. git clone https://github.com/git/git.git
    1. contrib/completion/git-completion.bash 自动补全
    2. contrib/completion/git-prompt.sh 高亮显示当前分支名称
  2. 将git-completion.bash下载到用户目录,可以重命名为 .git-completion.bash .git-prompt.sh作为隐藏文件
  3. 在 .bashrc 中加入一下代码
source ~/.git-completion.bash # 显示分支官方实现 GIT_PS1_SHOWDIRTYSTATE=true GIT_PS1_SHOWCOLORHINTS=true if [ -f ~/.git-completion.bash ]; then source ~/.git-prompt.sh PROMPT_COMMAND='__git_ps1 "[\t][\u@\h:\w]" "\\\$ "' fi

  1. 然后刷新一下 .bashrc :source ~/.bashrc
  2. 即可生效
在配置时由于git-completion.bash版本问题,导致分支名称无法补全,提示 搞了一宿,终于在vimdiff下发现问题,并成功解决,心情甚爽,并附上两个文件,点击可下载!
分支名称补全提示
分支名称无法补全提示
diff 代码:
【GIT 自动补全命令,分支名 以及 高亮显示当前分支】没有研究自己也看不懂, 能看懂的大神评论下吧
--- .git-completion.bash2017-06-22 09:37:23.336295271 +0800 +++ git_bak 2017-06-19 13:18:18.797790093 +0800 @@ -28,6 +28,14 @@ # completion style.For example '!f() { : git commit ; ... }; f' will # tell the completion to use commit completion.This also works with aliases # of form "!sh -c '...'".For example, "!sh -c ': git commit ; ... '". +# +# You can set the following environment variables to influence the behavior of +# the completion routines: +# +#GIT_COMPLETION_CHECKOUT_NO_GUESS +# +#When set to "1", do not include "DWIM" suggestions in git-checkout +#completion (e.g., completing "foo" when "origin/foo" exists). case "$COMP_WORDBREAKS" in *:*) : great ; ; @@ -213,6 +221,20 @@ } fi+# Fills the COMPREPLY array with prefiltered words without any additional +# processing. +# Callers must take care of providing only words that match the current word +# to be completed and adding any prefix and/or suffix (trailing space!), if +# necessary. +# 1: List of newline-separated matching completion words, complete with +#prefix and suffix. +__gitcomp_direct () +{ +local IFS=$'\n' + +COMPREPLY=($1) +} + __gitcompappend () { local x i=${#COMPREPLY[@]} @@ -338,14 +360,27 @@ done | sort | uniq }+# Lists branches from the local repository. +# 1: A prefix to be added to each listed branch (optional). +# 2: List only branches matching this word (optional; list all branches if +#unset or empty). +# 3: A suffix to be appended to each listed branch (optional). __git_heads () { -__git for-each-ref --format='%(refname:short)' refs/heads +local pfx="${1-}" cur_="${2-}" sfx="${3-}" + +__git for-each-ref --format="${pfx//\%/%%}%(refname:strip=2)$sfx" \ +"refs/heads/$cur_*" "refs/heads/$cur_*/**" }+# Lists tags from the local repository. +# Accepts the same positional parameters as __git_heads() above. __git_tags () { -__git for-each-ref --format='%(refname:short)' refs/tags +local pfx="${1-}" cur_="${2-}" sfx="${3-}" + +__git for-each-ref --format="${pfx//\%/%%}%(refname:strip=2)$sfx" \ +"refs/tags/$cur_*" "refs/tags/$cur_*/**" } # Lists refs from the local (by default) or from a remote repository. @@ -354,11 +389,21 @@ #Can be the name of a configured remote, a path, or a URL. # 2: In addition to local refs, list unique branches from refs/remotes/ for #'git checkout's tracking DWIMery (optional; ignored, if set but empty). +# 3: A prefix to be added to each listed ref (optional). +# 4: List only refs matching this word (optional; list all refs if unset or +#empty). +# 5: A suffix to be appended to each listed ref (optional; ignored, if set +#but empty). +# +# Use __git_complete_refs() instead. __git_refs () { local i hash dir track="${2-}" local list_refs_from=path remote="${1-}" -local format refs pfx +local format refs +local pfx="${3-}" cur_="${4-$cur}" sfx="${5-}" +local match="${4-}" +local fer_pfx="${pfx//\%/%%}" # "escape" for-each-ref format specifiers__git_find_repo_path dir="$__git_repo_path" @@ -382,63 +427,78 @@ fiif [ "$list_refs_from" = path ]; then -case "$cur" in +if [[ "$cur_" == ^* ]]; then +pfx="$pfx^" +fer_pfx="$fer_pfx^" +cur_=${cur_#^} +match=${match#^} +fi +case "$cur_" in refs|refs/*) format="refname" -refs="${cur%/*}" +refs=("$match*" "$match*/**") track="" ; ; *) -[[ "$cur" == ^* ]] && pfx="^" for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD; do -if [ -e "$dir/$i" ]; then echo $pfx$i; fi +case "$i" in +$match*) +if [ -e "$dir/$i" ]; then +echo "$pfx$i$sfx" +fi +; ; +esac done -format="refname:short" -refs="refs/tags refs/heads refs/remotes" +format="refname:strip=2" +refs=("refs/tags/$match*" "refs/tags/$match*/**" +"refs/heads/$match*" "refs/heads/$match*/**" +"refs/remotes/$match*" "refs/remotes/$match*/**") ; ; esac -__git_dir="$dir" __git for-each-ref --format="$pfx%($format)" \ -$refs +__git_dir="$dir" __git for-each-ref --format="$fer_pfx%($format)$sfx" \ +"${refs[@]}" if [ -n "$track" ]; then # employ the heuristic used by git checkout # Try to find a remote branch that matches the completion word # but only output if the branch name is unique -local ref entry -__git for-each-ref --shell --format="ref=%(refname:short)" \ -"refs/remotes/" | \ -while read -r entry; do -eval "$entry" -ref="${ref#*/}" -if [[ "$ref" == "$cur"* ]]; then -echo "$ref" -fi -done | sort | uniq -u +__git for-each-ref --format="$fer_pfx%(refname:strip=3)$sfx" \ +--sort="refname:strip=3" \ +"refs/remotes/*/$match*" "refs/remotes/*/$match*/**" | \ +uniq -u fi return fi -case "$cur" in +case "$cur_" in refs|refs/*) -__git ls-remote "$remote" "$cur*" | \ +__git ls-remote "$remote" "$match*" | \ while read -r hash i; do case "$i" in *^{}) ; ; -*) echo "$i" ; ; +*) echo "$pfx$i$sfx" ; ; esac done ; ; *) if [ "$list_refs_from" = remote ]; then -echo "HEAD" -__git for-each-ref --format="%(refname:short)" \ -"refs/remotes/$remote/" | sed -e "s#^$remote/##" +case "HEAD" in +$match*) echo "${pfx}HEAD$sfx" ; ; +esac +__git for-each-ref --format="$fer_pfx%(refname:strip=3)$sfx" \ +"refs/remotes/$remote/$match*" \ +"refs/remotes/$remote/$match*/**" else -__git ls-remote "$remote" HEAD \ -"refs/tags/*" "refs/heads/*" "refs/remotes/*" | +local query_symref +case "HEAD" in +$match*)query_symref="HEAD" ; ; +esac +__git ls-remote "$remote" $query_symref \ +"refs/tags/$match*" "refs/heads/$match*" \ +"refs/remotes/$match*" | while read -r hash i; do case "$i" in *^{}); ; -refs/*) echo "${i#refs/*/}" ; ; -*)echo "$i" ; ; # symbolic refs +refs/*) echo "$pfx${i#refs/*/}$sfx" ; ; +*)echo "$pfx$i$sfx" ; ; # symbolic refs esac done fi @@ -446,7 +506,38 @@ esac }+# Completes refs, short and long, local and remote, symbolic and pseudo. +# +# Usage: __git_complete_refs [
: A prefix to be added to each ref. +# --cur=: The current ref to be completed.Defaults to the current +#word to be completed. +# --sfx=: A suffix to be appended to each ref instead of the default +#space. +__git_complete_refs () +{ +local remote track pfx cur_="$cur" sfx=" " + +while test $# != 0; do +case "$1" in +--remote=*) remote="${1##--remote=}" ; ; +--track)track="yes" ; ; +--pfx=*)pfx="${1##--pfx=}" ; ; +--cur=*)cur_="${1##--cur=}" ; ; +--sfx=*)sfx="${1##--sfx=}" ; ; +*)return 1 ; ; +esac +shift +done + +__gitcomp_direct "$(__git_refs "$remote" "$track" "$pfx" "$cur_" "$sfx")" +} + # __git_refs2 requires 1 argument (to pass to __git_refs) +# Deprecated: use __git_complete_fetch_refspecs() instead. __git_refs2 () { local i @@ -455,6 +546,24 @@ done }+# Completes refspecs for fetching from a remote repository. +# 1: The remote repository. +# 2: A prefix to be added to each listed refspec (optional). +# 3: The ref to be completed as a refspec instead of the current word to be +#completed (optional) +# 4: A suffix to be appended to each listed refspec instead of the default +#space (optional). +__git_complete_fetch_refspecs () +{ +local i remote="$1" pfx="${2-}" cur_="${3-$cur}" sfx="${4- }" + +__gitcomp_direct "$( +for i in $(__git_refs "$remote" "" "" "$cur_") ; do +echo "$pfx$i:$i$sfx" +done +)" +} + # __git_refs_remotes requires 1 argument (to pass to ls-remote) __git_refs_remotes () { @@ -554,15 +663,15 @@ *...*) pfx="${cur_%...*}..." cur_="${cur_#*...}" -__gitcomp_nl "$(__git_refs)" "$pfx" "$cur_" +__git_complete_refs --pfx="$pfx" --cur="$cur_" ; ; *..*) pfx="${cur_%..*}.." cur_="${cur_#*..}" -__gitcomp_nl "$(__git_refs)" "$pfx" "$cur_" +__git_complete_refs --pfx="$pfx" --cur="$cur_" ; ; *) -__gitcomp_nl "$(__git_refs)" +__git_complete_refs ; ; esac } @@ -608,6 +717,7 @@ i="${words[c]}" case "$i" in --mirror) [ "$cmd" = "push" ] && no_complete_refspec=1 ; ; +-d|--delete) [ "$cmd" = "push" ] && lhs=0 ; ; --all) case "$cmd" in push) no_complete_refspec=1 ; ; @@ -647,23 +757,23 @@ case "$cmd" in fetch) if [ $lhs = 1 ]; then -__gitcomp_nl "$(__git_refs2 "$remote")" "$pfx" "$cur_" +__git_complete_fetch_refspecs "$remote" "$pfx" "$cur_" else -__gitcomp_nl "$(__git_refs)" "$pfx" "$cur_" +__git_complete_refs --pfx="$pfx" --cur="$cur_" fi ; ; pull|remote) if [ $lhs = 1 ]; then -__gitcomp_nl "$(__git_refs "$remote")" "$pfx" "$cur_" +__git_complete_refs --remote="$remote" --pfx="$pfx" --cur="$cur_" else -__gitcomp_nl "$(__git_refs)" "$pfx" "$cur_" +__git_complete_refs --pfx="$pfx" --cur="$cur_" fi ; ; push) if [ $lhs = 1 ]; then -__gitcomp_nl "$(__git_refs)" "$pfx" "$cur_" +__git_complete_refs --pfx="$pfx" --cur="$cur_" else -__gitcomp_nl "$(__git_refs "$remote")" "$pfx" "$cur_" +__git_complete_refs --remote="$remote" --pfx="$pfx" --cur="$cur_" fi ; ; esac @@ -1066,7 +1176,7 @@case "$subcommand" in bad|good|reset|skip|start) -__gitcomp_nl "$(__git_refs)" +__git_complete_refs ; ; *) ; ; @@ -1088,12 +1198,12 @@case "$cur" in --set-upstream-to=*) -__gitcomp_nl "$(__git_refs)" "" "${cur##--set-upstream-to=}" +__git_complete_refs --cur="${cur##--set-upstream-to=}" ; ; --*) __gitcomp " --color --no-color --verbose --abbrev= --no-abbrev ---track --no-track --contains --merged --no-merged +--track --no-track --contains --no-contains --merged --no-merged --set-upstream-to= --edit-description --list --unset-upstream --delete --move --remotes --column --no-column --sort= --points-at @@ -1101,9 +1211,9 @@ ; ; *) if [ $only_local_ref = "y" -a $has_r = "n" ]; then -__gitcomp_nl "$(__git_heads)" +__gitcomp_direct "$(__git_heads "" "$cur" " ")" else -__gitcomp_nl "$(__git_refs)" +__git_complete_refs fi ; ; esac @@ -1146,18 +1256,19 @@ *) # check if --track, --no-track, or --no-guess was specified # if so, disable DWIM mode -local flags="--track --no-track --no-guess" track=1 -if [ -n "$(__git_find_on_cmdline "$flags")" ]; then -track='' +local flags="--track --no-track --no-guess" track_opt="--track" +if [ "$GIT_COMPLETION_CHECKOUT_NO_GUESS" = "1" ] || +[ -n "$(__git_find_on_cmdline "$flags")" ]; then +track_opt='' fi -__gitcomp_nl "$(__git_refs '' $track)" +__git_complete_refs $track_opt ; ; esac } _git_cherry () { -__gitcomp_nl "$(__git_refs)" +__git_complete_refs } _git_cherry_pick () @@ -1172,7 +1283,7 @@ __gitcomp "--edit --no-commit --signoff --strategy= --mainline" ; ; *) -__gitcomp_nl "$(__git_refs)" +__git_complete_refs ; ; esac } @@ -1208,6 +1319,7 @@ --template= --depth --single-branch +--no-tags --branch --recurse-submodules --no-single-branch @@ -1224,7 +1336,7 @@ { case "$prev" in -c|-C) -__gitcomp_nl "$(__git_refs)" "" "${cur}" +__git_complete_refs return ; ; esac @@ -1237,7 +1349,7 @@ ; ; --reuse-message=*|--reedit-message=*|\ --fixup=*|--squash=*) -__gitcomp_nl "$(__git_refs)" "" "${cur#*=}" +__git_complete_refs --cur="${cur#*=}" return ; ; --untracked-files=*) @@ -1277,7 +1389,7 @@ " return esac -__gitcomp_nl "$(__git_refs)" +__git_complete_refs } __git_diff_algorithms="myers minimal patience histogram" @@ -1428,8 +1540,43 @@ _gitk }-__git_match_ctag() { -awk "/^${1//\//\\/}/ { print \$1 }" "$2" +# Lists matching symbol names from a tag (as in ctags) file. +# 1: List symbol names matching this word. +# 2: The tag file to list symbol names from. +# 3: A prefix to be added to each listed symbol name (optional). +# 4: A suffix to be appended to each listed symbol name (optional). +__git_match_ctag () { +awk -v pfx="${3-}" -v sfx="${4-}" " +/^${1//\//\\/}/ { print pfx \$1 sfx } +" "$2" +} + +# Complete symbol names from a tag file. +# Usage: __git_complete_symbol [
: A prefix to be added to each symbol name. +# --cur=: The current symbol name to be completed.Defaults to +#the current word to be completed. +# --sfx=: A suffix to be appended to each symbol name instead +#of the default space. +__git_complete_symbol () { +local tags=tags pfx="" cur_="${cur-}" sfx=" " + +while test $# != 0; do +case "$1" in +--tags=*)tags="${1##--tags=}" ; ; +--pfx=*)pfx="${1##--pfx=}" ; ; +--cur=*)cur_="${1##--cur=}" ; ; +--sfx=*)sfx="${1##--sfx=}" ; ; +*)return 1 ; ; +esac +shift +done + +if test -r "$tags"; then +__gitcomp_direct "$(__git_match_ctag "$cur_" "$tags" "$pfx" "$sfx")" +fi } _git_grep () @@ -1459,14 +1606,11 @@case "$cword,$prev" in 2,*|*,-*) -if test -r tags; then -__gitcomp_nl "$(__git_match_ctag "$cur" tags)" -return -fi +__git_complete_symbol && return ; ; esac-__gitcomp_nl "$(__git_refs)" +__git_complete_refs } _git_help () @@ -1573,6 +1717,19 @@ if [ -f "$__git_repo_path/MERGE_HEAD" ]; then merge="--merge" fi +case "$prev,$cur" in +-L,:*:*) +return# fall back to Bash filename completion +; ; +-L,:*) +__git_complete_symbol --cur="${cur#:}" --sfx=":" +return +; ; +-G,*|-S,*) +__git_complete_symbol +return +; ; +esac case "$cur" in --pretty=*|--format=*) __gitcomp "$__git_log_pretty_formats $(__git_pretty_aliases) @@ -1618,6 +1775,21 @@ " return ; ; +-L:*:*) +return# fall back to Bash filename completion +; ; +-L:*) +__git_complete_symbol --cur="${cur#-L:}" --sfx=":" +return +; ; +-G*) +__git_complete_symbol --pfx="-G" --cur="${cur#-G}" +return +; ; +-S*) +__git_complete_symbol --pfx="-S" --cur="${cur#-S}" +return +; ; esac __git_complete_revlist } @@ -1640,7 +1812,7 @@ --rerere-autoupdate --no-rerere-autoupdate --abort --continue" return esac -__gitcomp_nl "$(__git_refs)" +__git_complete_refs } _git_mergetool () @@ -1665,7 +1837,7 @@ return ; ; esac -__gitcomp_nl "$(__git_refs)" +__git_complete_refs } _git_mv () @@ -1703,7 +1875,7 @@ ,*) case "$prev" in --ref) -__gitcomp_nl "$(__git_refs)" +__git_complete_refs ; ; *) __gitcomp "$subcommands --ref" @@ -1712,7 +1884,7 @@ ; ; add,--reuse-message=*|append,--reuse-message=*|\ add,--reedit-message=*|append,--reedit-message=*) -__gitcomp_nl "$(__git_refs)" "" "${cur#*=}" +__git_complete_refs --cur="${cur#*=}" ; ; add,--*|append,--*) __gitcomp '--file= --message= --reedit-message= @@ -1731,7 +1903,7 @@ -m|-F) ; ; *) -__gitcomp_nl "$(__git_refs)" +__git_complete_refs ; ; esac ; ; @@ -1769,10 +1941,10 @@ --*=) ; ; *:*) -__gitcomp_nl "$(__git_refs)" "" "${cur_#*:}" +__git_complete_refs --cur="${cur_#*:}" ; ; *) -__gitcomp_nl "$(__git_refs)" "" "$cur_" +__git_complete_refs --cur="$cur_" ; ; esac } @@ -1848,7 +2020,7 @@return esac -__gitcomp_nl "$(__git_refs)" +__git_complete_refs } _git_reflog () @@ -1859,7 +2031,7 @@ if [ -z "$subcommand" ]; then __gitcomp "$subcommands" else -__gitcomp_nl "$(__git_refs)" +__git_complete_refs fi }@@ -2005,7 +2177,7 @@ return ; ; branch.*.merge) -__gitcomp_nl "$(__git_refs)" +__git_complete_refs return ; ; branch.*.rebase) @@ -2109,7 +2281,7 @@ ; ; branch.*) local pfx="${cur%.*}." cur_="${cur#*.}" -__gitcomp_nl "$(__git_heads)" "$pfx" "$cur_" "." +__gitcomp_direct "$(__git_heads "$pfx" "$cur_" ".")" __gitcomp_nl_append $'autosetupmerge\nautosetuprebase\n' "$pfx" "$cur_" return ; ; @@ -2164,14 +2336,23 @@ esac __gitcomp " add.ignoreErrors +advice.amWorkDir advice.commitBeforeMerge advice.detachedHead advice.implicitIdentity -advice.pushNonFastForward +advice.pushAlreadyExists +advice.pushFetchFirst +advice.pushNeedsForce +advice.pushNonFFCurrent +advice.pushNonFFMatching +advice.pushUpdateRejected advice.resolveConflict +advice.rmHints advice.statusHints +advice.statusUoption alias. am.keepcr +am.threeWay apply.ignorewhitespace apply.whitespace branch.autosetupmerge @@ -2216,19 +2397,26 @@ color.status.added color.status.changed color.status.header +color.status.localBranch color.status.nobranch +color.status.remoteBranch color.status.unmerged color.status.untracked color.status.updated color.ui +commit.cleanup +commit.gpgSign commit.status commit.template +commit.verbose core.abbrev core.askpass core.attributesfile core.autocrlf core.bare core.bigFileThreshold +core.checkStat +core.commentChar core.compression core.createObject core.deltaBaseCacheLimit @@ -2238,6 +2426,8 @@ core.fileMode core.fsyncobjectfiles core.gitProxy +core.hideDotFiles +core.hooksPath core.ignoreStat core.ignorecase core.logAllRefUpdates @@ -2245,20 +2435,30 @@ core.notesRef core.packedGitLimit core.packedGitWindowSize +core.packedRefsTimeout core.pager +core.precomposeUnicode core.preferSymlinkRefs core.preloadindex +core.protectHFS +core.protectNTFS core.quotepath core.repositoryFormatVersion core.safecrlf core.sharedRepository core.sparseCheckout +core.splitIndex +core.sshCommand core.symlinks core.trustctime core.untrackedCache core.warnAmbiguousRefs core.whitespace core.worktree +credential.helper +credential.useHttpPath +credential.username +credentialCache.ignoreSIGHUP diff.autorefreshindex diff.external diff.ignoreSubmodules @@ -2290,15 +2490,19 @@ format.thread format.to gc. +gc.aggressiveDepth gc.aggressiveWindow gc.auto +gc.autoDetach gc.autopacklimit +gc.logExpiry gc.packrefs gc.pruneexpire gc.reflogexpire gc.reflogexpireunreachable gc.rerereresolved gc.rerereunresolved +gc.worktreePruneExpire gitcvs.allbinary gitcvs.commitmsgannotation gitcvs.dbTableNamePrefix @@ -2516,7 +2720,7 @@ return ; ; esac -__gitcomp_nl "$(__git_refs)" +__git_complete_refs } _git_rerere () @@ -2540,7 +2744,7 @@ return ; ; esac -__gitcomp_nl "$(__git_refs)" +__git_complete_refs } _git_revert () @@ -2559,7 +2763,7 @@ return ; ; esac -__gitcomp_nl "$(__git_refs)" +__git_complete_refs } _git_rm () @@ -2639,7 +2843,7 @@ _git_stash () { local save_opts='--all --keep-index --no-keep-index --quiet --patch --include-untracked' -local subcommands='save list show apply clear drop pop create branch' +local subcommands='push save list show apply clear drop pop create branch' local subcommand="$(__git_find_on_cmdline "$subcommands")" if [ -z "$subcommand" ]; then case "$cur" in @@ -2654,6 +2858,9 @@ esac else case "$subcommand,$cur" in +push,--*) +__gitcomp "$save_opts --message" +; ; save,--*) __gitcomp "$save_opts" ; ; @@ -2667,7 +2874,7 @@ ; ; branch,*) if [ $cword -eq 3 ]; then -__gitcomp_nl "$(__git_refs)"; +__git_complete_refs else __gitcomp_nl "$(__git stash list \ | sed -n -e 's/:.*//p')" @@ -2834,7 +3041,7 @@ i="${words[c]}" case "$i" in -d|-v) -__gitcomp_nl "$(__git_tags)" +__gitcomp_direct "$(__git_tags "" "$cur" " ")" return ; ; -f) @@ -2849,11 +3056,11 @@ ; ; -*|tag) if [ $f = 1 ]; then -__gitcomp_nl "$(__git_tags)" +__gitcomp_direct "$(__git_tags "" "$cur" " ")" fi ; ; *) -__gitcomp_nl "$(__git_refs)" +__git_complete_refs ; ; esac@@ -2862,7 +3069,7 @@ __gitcomp " --list --delete --verify --annotate --message --file --sign --cleanup --local-user --force --column --sort= ---contains --points-at --merged --no-merged --create-reflog +--contains --no-contains --points-at --merged --no-merged --create-reflog " ; ; esac @@ -3024,6 +3231,15 @@ esac }+__gitcomp_direct () +{ +emulate -L zsh + +local IFS=$'\n' +compset -P '*[=:]' +compadd -Q -- ${=1} && _ret=0 +} + __gitcomp_nl () { emulate -L zsh

    推荐阅读