completions/git: honor global git arguments like --git-dir

Fixes #6219
This commit is contained in:
Johannes Altmanninger 2019-10-20 21:15:28 +02:00
parent 4095247deb
commit f4b4ff63b9

View file

@ -1,5 +1,30 @@
# fish completion for git # fish completion for git
# Use 'command git' to avoid interactions for aliases from git to (e.g.) hub
# Use this instead of calling git directly; it passes the commands that are
# already present on the commandline to git. This is relevant for --work-tree etc, see issue #6219.
function __fish_git
set -l saved_args $argv
set -l global_args
set -l cmd (commandline -opc)
# We assume that git is the first command until we have a better awareness of subcommands, see #2705.
set -e cmd[1]
if argparse -s (__fish_git_global_optspecs) -- $cmd 2>/dev/null
# All arguments that were parsed by argparse are global git options.
set -l num_global_args (math (count $cmd) - (count $argv))
if test $num_global_args -ne 0
set global_args $cmd[1..$num_global_args]
end
end
# Using 'command git' to avoid interactions for aliases from git to (e.g.) hub
command git $global_args $saved_args
end
# Print an optspec for argparse to handle git's options that are independent of any subcommand.
function __fish_git_global_optspecs
string join \n v-version h/help C= c=+ 'e-exec-path=?' H-html-path M-man-path I-info-path p/paginate \
P/no-pager o-no-replace-objects b-bare G-git-dir= W-work-tree= N-namespace= S-super-prefix= \
l-literal-pathspecs g-glob-pathspecs O-noglob-pathspecs i-icase-pathspecs
end
function __fish_git_commits function __fish_git_commits
# Complete commits with their subject line as the description # Complete commits with their subject line as the description
@ -13,7 +38,7 @@ function __fish_git_commits
# that happens for 3 commits out of 600k. # that happens for 3 commits out of 600k.
# For fish, at the time of writing, out of 12200 commits, 7 commits need 8 characters. # For fish, at the time of writing, out of 12200 commits, 7 commits need 8 characters.
# And since this takes about 1/3rd of the time that disambiguating takes... # And since this takes about 1/3rd of the time that disambiguating takes...
command git log --pretty=tformat:"%H"\t"%<(64,trunc)%s" --all --max-count=1000 2>/dev/null \ __fish_git log --pretty=tformat:"%H"\t"%<(64,trunc)%s" --all --max-count=1000 2>/dev/null \
| string replace -r '^([0-9a-f]{10})[0-9a-f]*\t(.*)' '$1\t$2' | string replace -r '^([0-9a-f]{10})[0-9a-f]*\t(.*)' '$1\t$2'
end end
@ -21,19 +46,19 @@ function __fish_git_recent_commits
# Like __fish_git_commits, but not on all branches and limited to # Like __fish_git_commits, but not on all branches and limited to
# the last 50 commits. Used for fixup, where only the current branch # the last 50 commits. Used for fixup, where only the current branch
# and the latest commits make sense. # and the latest commits make sense.
command git log --pretty=tformat:"%h"\t"%<(64,trunc)%s" --max-count=50 2>/dev/null __fish_git log --pretty=tformat:"%h"\t"%<(64,trunc)%s" --max-count=50 2>/dev/null
end end
function __fish_git_branches function __fish_git_branches
# This is much faster than using `git branch`, # This is much faster than using `git branch`,
# and avoids having to deal with localized "detached HEAD" messages. # and avoids having to deal with localized "detached HEAD" messages.
command git for-each-ref --format='%(refname)' refs/heads/ refs/remotes/ 2>/dev/null \ __fish_git for-each-ref --format='%(refname)' refs/heads/ refs/remotes/ 2>/dev/null \
| string replace -r '^refs/heads/(.*)$' '$1\tLocal Branch' \ | string replace -r '^refs/heads/(.*)$' '$1\tLocal Branch' \
| string replace -r '^refs/remotes/(.*)$' '$1\tRemote Branch' | string replace -r '^refs/remotes/(.*)$' '$1\tRemote Branch'
end end
function __fish_git_local_branches function __fish_git_local_branches
command git for-each-ref --format='%(refname)' refs/heads/ refs/remotes/ 2>/dev/null \ __fish_git for-each-ref --format='%(refname)' refs/heads/ refs/remotes/ 2>/dev/null \
| string replace -rf '^refs/heads/(.*)$' '$1\tLocal Branch' | string replace -rf '^refs/heads/(.*)$' '$1\tLocal Branch'
end end
@ -42,18 +67,18 @@ function __fish_git_unique_remote_branches
# if they are unambiguous. # if they are unambiguous.
# E.g. if only alice has a "frobulate" branch # E.g. if only alice has a "frobulate" branch
# `git checkout frobulate` is equivalent to `git checkout -b frobulate --track alice/frobulate`. # `git checkout frobulate` is equivalent to `git checkout -b frobulate --track alice/frobulate`.
command git for-each-ref --format="%(refname:strip=3)" \ __fish_git for-each-ref --format="%(refname:strip=3)" \
--sort="refname:strip=3" \ --sort="refname:strip=3" \
"refs/remotes/*/$match*" "refs/remotes/*/*/**" 2>/dev/null | \ "refs/remotes/*/$match*" "refs/remotes/*/*/**" 2>/dev/null | \
uniq -u uniq -u
end end
function __fish_git_tags function __fish_git_tags
command git tag --sort=-creatordate 2>/dev/null __fish_git tag --sort=-creatordate 2>/dev/null
end end
function __fish_git_heads function __fish_git_heads
set -l gitdir (command git rev-parse --git-dir 2>/dev/null) set -l gitdir (__fish_git rev-parse --git-dir 2>/dev/null)
or return # No git dir, no need to even test. or return # No git dir, no need to even test.
for head in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD for head in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD
if test -f $gitdir/$head if test -f $gitdir/$head
@ -69,7 +94,7 @@ function __fish_git_refs
end end
function __fish_git_remotes function __fish_git_remotes
command git remote 2>/dev/null __fish_git remote 2>/dev/null
end end
function __fish_git_files function __fish_git_files
@ -90,7 +115,7 @@ function __fish_git_files
# to get _all_ kinds of staged files. # to get _all_ kinds of staged files.
# Save the repo root to remove it from the path later. # Save the repo root to remove it from the path later.
set -l root (command git rev-parse --show-toplevel --is-bare-repository 2>/dev/null) set -l root (__fish_git rev-parse --show-toplevel --is-bare-repository 2>/dev/null)
or return or return
# Skip bare repositories. # Skip bare repositories.
@ -152,10 +177,10 @@ function __fish_git_files
# We pick the v2 format if we can, because it shows relative filenames (if used without "-z"). # We pick the v2 format if we can, because it shows relative filenames (if used without "-z").
# We fall back on the v1 format by reading git's _version_, because trying v2 first is too slow. # We fall back on the v1 format by reading git's _version_, because trying v2 first is too slow.
set -l ver (command git --version | string replace -rf 'git version (\d+)\.(\d+)\.?.*' '$1\n$2') set -l ver (__fish_git --version | string replace -rf 'git version (\d+)\.(\d+)\.?.*' '$1\n$2')
# Version >= 2.11.* has the v2 format. # Version >= 2.11.* has the v2 format.
if test "$ver[1]" -gt 2 2>/dev/null; or test "$ver[1]" -eq 2 -a "$ver[2]" -ge 11 2>/dev/null if test "$ver[1]" -gt 2 2>/dev/null; or test "$ver[1]" -eq 2 -a "$ver[2]" -ge 11 2>/dev/null
command git $git_opt status --porcelain=2 $status_opt \ __fish_git $git_opt status --porcelain=2 $status_opt \
| while read -la -d ' ' line | while read -la -d ' ' line
set -l file set -l file
set -l desc set -l desc
@ -312,7 +337,7 @@ function __fish_git_files
set -l previous set -l previous
# Note that we can't use space as a delimiter between status and filename, because # Note that we can't use space as a delimiter between status and filename, because
# the status can contain spaces - " M" is different from "M ". # the status can contain spaces - " M" is different from "M ".
command git $git_opt status --porcelain -z $status_opt \ __fish_git $git_opt status --porcelain -z $status_opt \
| while read -lz line | while read -lz line
set -l desc set -l desc
# The entire line is the "from" from a rename. # The entire line is the "from" from a rename.
@ -499,16 +524,9 @@ end
function __fish_git_needs_command function __fish_git_needs_command
# Figure out if the current invocation already has a command. # Figure out if the current invocation already has a command.
set -l cmd (commandline -opc)
# Git has tons of options, but fortunately only a few can appear before the command.
# They are listed here.
set -l opts h-help p P-paginate N-no-pager b-bare o-no-replace-objects \
l-literal-pathspecs g-glob-pathspecs O-noglob-pathspecs i-icase-pathspecs \
e-exec-path= G-git-dir= c= C= v-version H-html-path \
m-man-path I-info-path w-work-tree= a-namespace= s-super-prefix=
set cmd (commandline -opc)
set -e cmd[1] set -e cmd[1]
argparse -s $opts -- $cmd 2>/dev/null argparse -s (__fish_git_global_optspecs) -- $cmd 2>/dev/null
or return 0 or return 0
# These flags function as commands, effectively. # These flags function as commands, effectively.
set -q _flag_version; and return 1 set -q _flag_version; and return 1
@ -591,15 +609,15 @@ function __fish_git_stash_not_using_subcommand
end end
function __fish_git_complete_worktrees function __fish_git_complete_worktrees
command git worktree list --porcelain | string replace --regex --filter '^worktree\s*' '' __fish_git worktree list --porcelain | string replace --regex --filter '^worktree\s*' ''
end end
function __fish_git_complete_stashes function __fish_git_complete_stashes
command git stash list --format=%gd:%gs 2>/dev/null | string replace ":" \t __fish_git stash list --format=%gd:%gs 2>/dev/null | string replace ":" \t
end end
function __fish_git_aliases function __fish_git_aliases
command git config -z --get-regexp '^alias\.' 2>/dev/null | while read -lz key value __fish_git config -z --get-regexp '^alias\.' 2>/dev/null | while read -lz key value
begin begin
set -l name (string replace -r '^.*\.' '' -- $key) set -l name (string replace -r '^.*\.' '' -- $key)
printf "%s\t%s\n" $name "Alias for $value" printf "%s\t%s\n" $name "Alias for $value"
@ -652,7 +670,7 @@ function __fish_git_possible_commithash
end end
function __fish_git_reflog function __fish_git_reflog
command git reflog --no-decorate 2>/dev/null | string replace -r '[0-9a-f]* (.+@\{[0-9]+\}): (.*)$' '$1\t$2' __fish_git reflog --no-decorate 2>/dev/null | string replace -r '[0-9a-f]* (.+@\{[0-9]+\}): (.*)$' '$1\t$2'
end end
function __fish_git_help_all_concepts function __fish_git_help_all_concepts
@ -728,7 +746,7 @@ end
complete -f -c git -l help -d 'Display the manual of a git command' complete -f -c git -l help -d 'Display the manual of a git command'
complete -f -c git -n '__fish_git_needs_command' -l version -d 'Display version' complete -f -c git -n '__fish_git_needs_command' -l version -d 'Display version'
complete -x -c git -n '__fish_git_needs_command' -s C -a '(__fish_complete_directories)' -d 'Run as if git was started in this directory' complete -x -c git -n '__fish_git_needs_command' -s C -a '(__fish_complete_directories)' -d 'Run as if git was started in this directory'
complete -x -c git -n '__fish_git_needs_command' -s c -a '(command git config -l 2>/dev/null | string replace = \t)' -d 'Set a configuration option' complete -x -c git -n '__fish_git_needs_command' -s c -a '(__fish_git config -l 2>/dev/null | string replace = \t)' -d 'Set a configuration option'
complete -x -c git -n '__fish_git_needs_command' -l exec-path -a '(__fish_complete_directories)' -d 'Get or set the path to the git programs' complete -x -c git -n '__fish_git_needs_command' -l exec-path -a '(__fish_complete_directories)' -d 'Get or set the path to the git programs'
complete -f -c git -n '__fish_git_needs_command' -l html-path -d 'Print the path to the html documentation' complete -f -c git -n '__fish_git_needs_command' -l html-path -d 'Print the path to the html documentation'
complete -f -c git -n '__fish_git_needs_command' -l man-path -d 'Print the path to the man documentation' complete -f -c git -n '__fish_git_needs_command' -l man-path -d 'Print the path to the man documentation'