completions/git: extract function for adding arbitrary-revision-completion

Most Git commands take arbitrary revisions.  AFAICT, we usually want the same
order, e.g. list local branches before remote branches before commit IDs etc.
I think there is no particular reason why this order is inconsistent between
various subcommands.

Let's extract a function. This standardizes the order and adds various
revision-types that were missing for some subcommands.
This commit is contained in:
Johannes Altmanninger
2025-05-31 20:30:23 +02:00
parent 7f2f5bb2f4
commit b4392f6f7d

View File

@@ -65,6 +65,10 @@ function __fish_git_local_branches
__fish_git for-each-ref --format='%(refname:strip=2)%09Local Branch' --sort=-committerdate refs/heads/ 2>/dev/null
end
function __fish_git_remote_branches
__fish_git for-each-ref --format='%(refname:strip=2)%09Remote Branch' refs/remotes/ 2>/dev/null
end
function __fish_git_unique_remote_branches
# `git checkout` accepts remote branches without the remote part
# if they are unambiguous.
@@ -90,6 +94,12 @@ function __fish_git_heads
end
end
function __fish_git_remote_heads
# Example of output parsed:
# "remote.upstream.url git@github.com:fish-shell/fish-shell.git" -> "upstream\tgit@github.com:fish-shell/fish-shell.git"
__fish_git for-each-ref --format="%(refname:strip=2)" 'refs/remotes/*/HEAD' | path dirname
end
function __fish_git_refs
__fish_git_branches
__fish_git_tags
@@ -102,6 +112,24 @@ function __fish_git_remotes
__fish_git config --get-regexp 'remote\.[a-z]+\.url' | string replace -rf 'remote\.(.*)\.url (.*)' '$1\t$2'
end
set -g __fish_git_recent_commits_arg
set -g __fish_git_unqualified_unique_remote_branches false
set -g __fish_git_filter_non_pushable ''
function __fish_git_add_revision_completion
set -l c complete -f -c git $argv -n 'not contains -- -- (commandline -xpc)' -ka
# The following dynamic, order-preserved (-k) completions will be shown in reverse order (see #9221)
$c "(__fish_git_recent_commits $__fish_git_recent_commits_arg $__fish_git_filter_non_pushable)"
$c "(__fish_git_tags)" -d Tag
$c "(__fish_git_remote_heads $__fish_git_filter_non_pushable)" -d 'Remote alias'
$c "(__fish_git_heads $__fish_git_filter_non_pushable)" -d Head
$c "(__fish_git_remote_branches $__fish_git_filter_non_pushable)"
if $__fish_git_unqualified_unique_remote_branches
$c "(__fish_git_unique_remote_branches $__fish_git_filter_non_pushable)" -d 'Unique Remote Branch'
end
$c "(__fish_git_local_branches)" -d 'Local Branch'
end
function __fish_git_files
# A function to show various kinds of files git knows about,
# by parsing `git status --porcelain`.
@@ -1038,7 +1066,7 @@ complete -f -c git -n '__fish_git_using_command fetch' -l shallow-since -d 'Deep
complete -f -c git -n '__fish_git_using_command fetch' -l shallow-exclude -d 'Deepen history of shallow clone, excluding rev'
complete -f -c git -n '__fish_git_using_command fetch' -l unshallow -d 'Convert to a complete repository'
complete -f -c git -n '__fish_git_using_command fetch' -l refetch -d 'Re-fetch without negotiating common commits'
complete -f -c git -n '__fish_git_using_command fetch' -l negotiation-tip -d 'Only report commits reachable from these tips' -kxa '(__fish_git_commits; __fish_git_branches)'
__fish_git_add_revision_completion -n '__fish_git_using_command fetch' -l negotiation-tip -d 'Only report commits reachable from these tips' -x
complete -f -c git -n '__fish_git_using_command fetch' -l negotiate-only -d "Don't fetch, only show commits in common with the server"
complete -f -c git -n '__fish_git_using_command fetch' -l filter -ra '(__fish_git_filters)' -d 'Request a subset of objects from server'
@@ -1091,11 +1119,9 @@ complete -f -c git -n "__fish_git_using_command remote" -n "__fish_seen_subcomma
### show
complete -f -c git -n __fish_git_needs_command -a show -d 'Show the last commit of a branch'
complete -f -c git -n '__fish_git_using_command show' -n 'not contains -- -- (commandline -xpc)' -ka '(__fish_git_branches)'
complete -f -c git -n '__fish_git_using_command show' -n 'not contains -- -- (commandline -xpc)' -ka '(__fish_git_tags)' -d Tag
complete -f -c git -n '__fish_git_using_command show' -n 'not contains -- -- (commandline -xpc)' -ka '(__fish_git_commits)'
complete -f -c git -n '__fish_git_using_command show' -n 'not contains -- -- (commandline -xpc)' -ka '(__fish_git_complete_stashes)'
__fish_git_add_revision_completion -n '__fish_git_using_command show'
complete -f -c git -n __fish_git_needs_rev_files -n 'not contains -- -- (commandline -xpc)' -xa '(__fish_git_complete_rev_files)'
complete -f -c git -n '__fish_git_using_command show' -n 'not contains -- -- (commandline -xpc)' -ka '(__fish_git_complete_stashes)'
complete -F -c git -n '__fish_git_using_command show' -n 'contains -- -- (commandline -xpc)'
complete -f -c git -n '__fish_git_using_command show' -l format -d 'Pretty-print the contents of the commit logs in a given format' -a '(__fish_git_show_opt format)'
complete -f -c git -n '__fish_git_using_command show' -l abbrev-commit -d 'Show only a partial hexadecimal commit object name'
@@ -1183,13 +1209,12 @@ complete -c git -n '__fish_git_using_command am' -l show-current-patch -a 'diff
### checkout
complete -F -c git -n '__fish_git_using_command checkout' -n 'contains -- -- (commandline -xpc)'
complete -f -c git -n __fish_git_needs_command -a checkout -d 'Checkout and switch to a branch'
begin
set -lx __fish_git_recent_commits_arg --all
set -lx __fish_git_unqualified_unique_remote_branches true
__fish_git_add_revision_completion -n '__fish_git_using_command checkout'
end
# The following dynamic, order-preserved (-k) completions will be shown in reverse order (see #9221)
complete -f -c git -n '__fish_git_using_command checkout' -n 'not contains -- -- (commandline -xpc)' -ka '(__fish_git_recent_commits --all)'
complete -f -c git -n '__fish_git_using_command checkout' -n 'not contains -- -- (commandline -xpc)' -ka '(__fish_git_tags)' -d Tag
complete -f -c git -n '__fish_git_using_command checkout' -n 'not contains -- -- (commandline -xpc)' -ka '(__fish_git_heads)' -d Head
complete -f -c git -n '__fish_git_using_command checkout' -n 'not contains -- -- (commandline -xpc)' -ka '(__fish_git_unique_remote_branches)' -d 'Unique Remote Branch'
complete -f -c git -n '__fish_git_using_command checkout' -n 'not contains -- -- (commandline -xpc)' -ka '(__fish_git_branches)'
# In the presence of changed files, `git checkout ...` assumes highest likelihood is intent to restore so this comes last (aka shown first).
complete -f -c git -n '__fish_git_using_command checkout' -ka '(__fish_git_files modified deleted modified-staged-deleted)'
@@ -1426,9 +1451,7 @@ complete -x -c git -n '__fish_git_using_command daemon' -l access-hook -d 'Hook
### describe
complete -c git -n __fish_git_needs_command -a describe -d 'Give an object a human readable name'
complete -f -c git -n '__fish_git_using_command describe' -ka '(__fish_git_tags)' -d Tag
complete -f -c git -n '__fish_git_using_command describe' -ka '(__fish_git_branches)'
complete -f -c git -n '__fish_git_using_command describe' -ka '(__fish_git_heads)' -d Head
__fish_git_add_revision_completion -n '__fish_git_using_command describe'
complete -f -c git -n '__fish_git_using_command describe' -l dirty -d 'Describe the state of the working tree, append dirty if there are local changes'
complete -f -c git -n '__fish_git_using_command describe' -l broken -d 'Describe the state of the working tree, append -broken instead of erroring'
complete -f -c git -n '__fish_git_using_command describe' -l all -d 'Use all tags, not just annotated'
@@ -1448,7 +1471,10 @@ complete -f -c git -n '__fish_git_using_command describe' -l first-parent -d 'Fo
complete -c git -n __fish_git_needs_command -a diff -d 'Show changes between commits and working tree'
complete -c git -n '__fish_git_using_command diff' -n 'not contains -- -- (commandline -xpc)' -ka '(__fish_git_ranges)'
complete -c git -n '__fish_git_using_command diff' -n 'not contains -- -- (commandline -xpc)' -ka '(__fish_git_complete_stashes)'
complete -c git -n '__fish_git_using_command diff' -n 'not contains -- -- (commandline -xpc)' -ka '(__fish_git_recent_commits --all)'
begin
set -lx __fish_git_recent_commits_arg --all
__fish_git_add_revision_completion -n '__fish_git_using_command diff'
end
complete -c git -n '__fish_git_using_command diff' -l cached -d 'Show diff of changes in the index'
complete -c git -n '__fish_git_using_command diff' -l staged -d 'Show diff of changes in the index'
complete -c git -n '__fish_git_using_command diff' -l no-index -d 'Compare two paths on the filesystem'
@@ -1779,7 +1805,7 @@ complete -f -c git -n '__fish_git_using_command maintenance' -l schedule -d 'Run
### merge
complete -f -c git -n __fish_git_needs_command -a merge -d 'Join multiple development histories'
complete -f -c git -n '__fish_git_using_command merge' -ka '(__fish_git_branches)'
__fish_git_add_revision_completion -n '__fish_git_using_command merge'
complete -f -c git -n '__fish_git_using_command merge' -l commit -d "Autocommit the merge"
complete -f -c git -n '__fish_git_using_command merge' -l no-commit -d "Don't autocommit the merge"
complete -f -c git -n '__fish_git_using_command merge' -s e -l edit -d 'Edit auto-generated merge message'
@@ -1815,7 +1841,7 @@ complete -f -c git -n '__fish_git_using_command merge' -l no-autostash -d 'Do no
### merge-base
complete -f -c git -n __fish_git_needs_command -a merge-base -d 'Find a common ancestor for a merge'
complete -f -c git -n '__fish_git_using_command merge-base' -ka '(__fish_git_branches)'
__fish_git_add_revision_completion -n '__fish_git_using_command merge-base'
complete -f -c git -n '__fish_git_using_command merge-base' -s a -l all -d 'Output all merge bases for the commits, instead of just one'
complete -f -c git -n '__fish_git_using_command merge-base' -l octopus -d 'Compute the best common ancestors of all supplied commits'
complete -f -c git -n '__fish_git_using_command merge-base' -l independent -d 'Print a minimal subset of the supplied commits with the same ancestors'
@@ -1933,9 +1959,10 @@ complete -f -c git -n '__fish_git_using_command range-diff' -l no-dual-color -d
### push
complete -f -c git -n __fish_git_needs_command -a push -d 'Push changes elsewhere'
complete -f -c git -n '__fish_git_using_command push' -n 'not __fish_git_branch_for_remote' -a '(__fish_git_remotes)' -d 'Remote alias'
complete -f -c git -n '__fish_git_using_command push' -n __fish_git_branch_for_remote -ka '(__fish_git_tags)' -d Tag
complete -f -c git -n '__fish_git_using_command push' -n __fish_git_branch_for_remote -ka '(__fish_git_branches)'
complete -f -c git -n '__fish_git_using_command push' -n __fish_git_branch_for_remote -ka '(__fish_git_heads)'
begin
set -lx __fish_git_filter_non_pushable '| string replace -r "(\t.*)?\$" ":\$1"'
__fish_git_add_revision_completion -n '__fish_git_using_command push' -n __fish_git_branch_for_remote
end
# The "refspec" here is an optional "+" to signify a force-push
complete -f -c git -n '__fish_git_using_command push' -n __fish_git_branch_for_remote -n 'string match -q "+*" -- (commandline -ct)' -ka '+(__fish_git_branches | string replace -r \t".*" "")' -d 'Force-push branch'
# git push REMOTE :BRANCH deletes BRANCH on remote REMOTE
@@ -1962,11 +1989,7 @@ complete -f -c git -n '__fish_git_using_command push' -l progress -d 'Force prog
### rebase
complete -f -c git -n __fish_git_needs_command -a rebase -d 'Reapply commit sequence on a new base'
complete -f -c git -n '__fish_git_using_command rebase' -a '(__fish_git_remotes)' -d 'Remote alias'
complete -f -c git -n '__fish_git_using_command rebase' -ka '(__fish_git_branches)'
complete -f -c git -n '__fish_git_using_command rebase' -a '(__fish_git_heads)' -d Head
complete -f -c git -n '__fish_git_using_command rebase' -ka '(__fish_git_tags)' -d Tag -k
complete -f -c git -n '__fish_git_using_command rebase' -a '(__fish_git_recent_commits)' -k
__fish_git_add_revision_completion -n '__fish_git_using_command rebase'
complete -f -c git -n '__fish_git_using_command rebase' -n __fish_git_is_rebasing -l continue -d 'Restart the rebasing process'
complete -f -c git -n '__fish_git_using_command rebase' -n __fish_git_is_rebasing -l abort -d 'Abort the rebase operation'
complete -f -c git -n '__fish_git_using_command rebase' -n __fish_git_is_rebasing -l edit-todo -d 'Edit the todo list'
@@ -1991,7 +2014,7 @@ complete -f -c git -n '__fish_git_using_command rebase' -l no-autosquash -d 'No
complete -f -c git -n '__fish_git_using_command rebase' -l autostash -d 'Before starting rebase, stash local changes, and apply stash when done'
complete -f -c git -n '__fish_git_using_command rebase' -l no-autostash -d 'Do not stash local changes before starting rebase'
complete -f -c git -n '__fish_git_using_command rebase' -l no-ff -d 'No fast-forward'
complete -f -c git -n '__fish_git_using_command rebase' -l onto -d 'Rebase current branch onto given upstream or newbase' -rka '(__fish_git_branches)'
__fish_git_add_revision_completion -n '__fish_git_using_command rebase' -l onto -d 'Rebase current branch onto given upstream or newbase' -r
complete -f -c git -n '__fish_git_using_command rebase' -l update-refs -d 'Update any branches that point to commits being rebased'
complete -f -c git -n '__fish_git_using_command rebase' -l no-update-refs -d 'Don\'t update any branches that point to commits being rebased'
# This actually takes script for $SHELL, but completing that is... complicated.
@@ -2010,7 +2033,7 @@ complete -c git -n __fish_git_needs_command -a reset -d 'Reset current HEAD to t
complete -f -c git -n '__fish_git_using_command reset' -l hard -d 'Reset the index and the working tree'
complete -f -c git -n '__fish_git_using_command reset' -l soft -d 'Reset head without touching the index or the working tree'
complete -f -c git -n '__fish_git_using_command reset' -l mixed -d 'The default: reset the index but not the working tree'
complete -c git -n '__fish_git_using_command reset' -n 'not contains -- -- (commandline -xpc)' -ka '(__fish_git_branches)'
__fish_git_add_revision_completion -n '__fish_git_using_command reset'
# reset can either undo changes to versioned modified files,
# or remove files from the staging area.
# Deleted files seem to need a "--" separator.
@@ -2042,7 +2065,10 @@ complete -f -c git -n '__fish_git_using_command switch' -ka '(__fish_git_unique_
complete -f -c git -n '__fish_git_using_command switch' -ka '(__fish_git_branches)'
complete -f -c git -n '__fish_git_using_command switch' -s c -l create -d 'Create a new branch'
complete -f -c git -n '__fish_git_using_command switch' -s C -l force-create -d 'Force create a new branch'
complete -f -c git -n '__fish_git_using_command switch' -s d -l detach -rka '(__fish_git_recent_commits --all)'
begin
set -lx __fish_git_recent_commits_arg --all
__fish_git_add_revision_completion -n '__fish_git_using_command switch' -s d -l detach -r
end
complete -f -c git -n '__fish_git_using_command switch' -s d -l detach -d 'Switch to a commit for inspection and discardable experiment' -rka '(__fish_git_refs)'
complete -f -c git -n '__fish_git_using_command switch' -l guess -d 'Guess branch name from remote branch (default)'
complete -f -c git -n '__fish_git_using_command switch' -l no-guess -d 'Do not guess branch name from remote branch'
@@ -2067,9 +2093,7 @@ complete -f -c git -n __fish_git_needs_command -a rev-list -d 'List commits in c
### rev-parse
complete -f -c git -n __fish_git_needs_command -a rev-parse -d 'Parse revision names or give repo information'
complete -f -c git -n '__fish_git_using_command rev-parse' -ka '(__fish_git_branches)'
complete -f -c git -n '__fish_git_using_command rev-parse' -a '(__fish_git_heads)' -d Head
complete -c git -n '__fish_git_using_command rev-parse' -ka '(__fish_git_tags)' -d Tag
__fish_git_add_revision_completion -n '__fish_git_using_command rev-parse'
complete -c git -n '__fish_git_using_command rev-parse' -l abbrev-ref -d 'Output non-ambiguous short object names'
### revert
@@ -2119,7 +2143,7 @@ complete -f -c git -n '__fish_git_using_command stripspace' -s c -l comment-line
### tag
complete -f -c git -n __fish_git_needs_command -a tag -d 'Create, list, delete or verify a tag object signed with GPG'
complete -f -c git -n '__fish_git_using_command tag' -n '__fish_not_contain_opt -s d' -n '__fish_not_contain_opt -s v' -n 'test (count (commandline -xpc | string match -r -v \'^-\')) -eq 3' -ka '(__fish_git_branches)'
__fish_git_add_revision_completion -n '__fish_git_using_command tag' -n '__fish_not_contain_opt -s d' -n '__fish_not_contain_opt -s v' -n 'test (count (commandline -xpc | string match -r -v \'^-\')) -eq 3'
complete -f -c git -n '__fish_git_using_command tag' -s a -l annotate -d 'Make an unsigned, annotated tag object'
complete -f -c git -n '__fish_git_using_command tag' -s s -l sign -d 'Make a GPG-signed tag'
complete -f -c git -n '__fish_git_using_command tag' -s d -l delete -d 'Remove a tag'
@@ -2180,11 +2204,10 @@ complete -f -c git -n "__fish_git_using_command worktree" -n "not __fish_seen_su
complete -f -c git -n '__fish_git_using_command worktree' -n '__fish_seen_subcommand_from add move remove' -s f -l force -d 'Override safeguards'
complete -c git -n '__fish_git_using_command worktree' -n '__fish_seen_subcommand_from add'
complete -c git -n '__fish_git_using_command worktree' -n '__fish_seen_subcommand_from add' -ka '(__fish_git_branches)'
complete -c git -n '__fish_git_using_command worktree' -n '__fish_seen_subcommand_from add' -ka '(__fish_git_heads)' -d Head
complete -c git -n '__fish_git_using_command worktree' -n '__fish_seen_subcommand_from add' -ka '(__fish_git_tags)' -d Tag
complete -c git -n '__fish_git_using_command worktree' -n '__fish_seen_subcommand_from add' -ka '(__fish_git_unique_remote_branches)' -d 'Unique Remote Branch'
complete -c git -n '__fish_git_using_command worktree' -n '__fish_seen_subcommand_from add' -ka '(__fish_git_local_branches)'
begin
set -lx __fish_git_unqualified_unique_remote_branches true
__fish_git_add_revision_completion -n '__fish_git_using_command worktree' -n '__fish_seen_subcommand_from add'
end
complete -x -c git -n '__fish_git_using_command worktree' -n '__fish_seen_subcommand_from add' -s b -d 'Create a new branch'
complete -x -c git -n '__fish_git_using_command worktree' -n '__fish_seen_subcommand_from add' -s B -d 'Create a new branch even if it already exists'
complete -f -c git -n '__fish_git_using_command worktree' -n '__fish_seen_subcommand_from add' -l detach -d 'Detach HEAD in the new working tree'
@@ -2241,7 +2264,7 @@ complete -f -c git -n __fish_git_needs_command -a config -d 'Set and read git co
### format-patch
complete -f -c git -n __fish_git_needs_command -a format-patch -d 'Generate patch series to send upstream'
complete -f -c git -n '__fish_git_using_command format-patch' -ka '(__fish_git_branches)'
__fish_git_add_revision_completion -n '__fish_git_using_command format-patch'
complete -c git -n '__fish_git_using_command format-patch' -s o -l output-directory -xa '(__fish_complete_directories)'
complete -f -c git -n '__fish_git_using_command format-patch' -s p -l no-stat -d "Generate plain patches without diffstat"
complete -f -c git -n '__fish_git_using_command format-patch' -s s -l no-patch -d "Suppress diff output"
@@ -2603,3 +2626,8 @@ for file in (path filter -xZ $PATH/git-* | path basename)
complete -c git -f -n "__fish_git_using_command $cmd" -a "(__fish_git_complete_custom_command $cmd)"
set -a __fish_git_custom_commands_completion $file
end
functions --erase __fish_git_add_revision_completion
set -eg __fish_git_recent_commits_arg
set -eg __fish_git_unqualified_unique_remote_branches
set -eg __fish_git_filter_non_pushable