Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
Re: Slowness issue with git completion
Nikolai Weibull wrote:
> How did you rewrite it? I tried implementing it with
> *(e:__git_cached:) (or similar), but that was, in my implementation, a
> lot slower. And this wasn’t even on Cygwin, where the forking makes
> it even slower.
Below is how I was going about it. As I said, it may be full of bugs and
other short-comings. It was pretty quick in the linux kernel repo,
though (I had implemented a minimal _git completion that used the
"git-add" completion to test the code).
I was also thinking about a scheme to give the user the option to choose
smart (and slow) and utterly-dumb (and quick) file completion in various
contexts via zstyle, but lost interest because it's just *so* much to
do.
Anyway, here it is:
# helpers...
# If a sub-command completion requires zsh to be in a git repository when
# executing, this function should be run to guard that. Usage:
# __git-needs_repo || return 0
function __git-needs_repo {
if [[ -z ${_gitdir} ]]; then
_message "Not a git repository."
return 1
fi
return 0
}
# Git file listing helpers.
#
# With git, files can be in various states. Files from one state may make
# sense for completion with one sub-command, but may be utterly useless
# with the next one. Therefore, we got a whole battery of helpers, that
# should make the job a lot easier.
#
# Identifying the enemy:
#
# index files
# Files git already knows about (aka. "stuff checked in"). The
# "git ls-tree" command can list those.
#
# modified files
# Files git knows about, but which have also changed. These changes
# could be staged or not. So we end up with the following set of
# states:
# mod-staged - files with staged modifications
# "git diff-index --cached --diff-filter=M --name-only"
# mod-unstaged - files, whose modifications are *not* staged
# "git ls-files -m"
# modified - both.
# "git diff-index --diff-filter=M --name-only"
#
# other files
# Git doesn't know these files. "git ls-files -o"
#
# ignored files
# Git doesn't know these, and it was even told to ignore them, when
# it sees them. "git ls-files --exclude-standard -i -o"
#
# deleted files
# Files git knows about, but which will be removed from its knowledge
# in the next commit (note that the actual file does not have to be
# deleted - "git rm --cached file" has that effect).
# "git diff-index --name-only --diff-filter=D"
#
# killed files
# Sometimes, in order to call "git checkout-index" successfully,
# files or directories may need to be removed to resolve conflicts.
# This may happen rarely, but we can use "git ls-files -k" to catch
# these, so we probably should. It may come in handy.
#
# unmerged files
# When multiple versions of a file exist in the staging area (the
# index file) that's an unmerged file. The only way I can think
# of end up having these in git-completion is during the resolution
# of a merge-conflict. And then, it would probably be most useful
# to have these available in an editor's completion function. Like
# "killed files", this sort of file is available from "git ls-files",
# but it'll need extra filtering, because --unmerged implicitly
# turns on --stage. "git ls-files -u"
#
# I'll be nameing these `__git-files_<type>', so ie "index files" will be
# completed by `__git-files_index'.
function __git-files_index {
local expl cw cd
local -a files
cw=${words[CURRENT]}
case $cw in
(*/) cd=${cw} ;;
(*/*) cd=${cw:h} ;;
esac
files=( ${(ps:\0:)"$(_call_program index-files git ls-tree --name-only -z HEAD${cd:+:$cd})"} )
_wanted 'index-files' expl 'index file' compadd -f ${expl} -- ${cd:+$cd}"${files[@]}"
}
function __git-files_modified {
local expl cw cd
local -a files
cw=${words[CURRENT]}
case $cw in
(*/) cd=${cw} ;;
(*/*) cd=${cw:h} ;;
esac
files=( ${(ps:\0:)"$(_call_program modified-files git diff-index -z --diff-filter=MDA --name-only HEAD ${cd:+$cd})"} )
files=( ${(u)${${(M)files:#${cd}*}/(#b)(${cd}[^\/]##)*/${match[1]}}} )
_wanted 'modified-files' expl 'modified file' compadd -f ${expl} -- "${files[@]}"
}
function __git-files_modified_staged {
local expl cw cd
local -a files
cw=${words[CURRENT]}
case $cw in
(*/) cd=${cw} ;;
(*/*) cd=${cw:h} ;;
esac
files=( ${(ps:\0:)"$(_call_program staged-modified-files git diff-index -z --cached --diff-filter=MDA --name-only HEAD ${cd:+$cd})"} )
files=( ${(u)${${(M)files:#${cd}*}/(#b)(${cd}[^\/]##)*/${match[1]}}} )
_wanted 'staged-modified-files' expl 'staged modified file' compadd -f ${expl} -- "${files[@]}"
}
function __git-files_modified_unstaged {
local expl cw cd
local -a files
cw=${words[CURRENT]}
case $cw in
(*/) cd=${cw} ;;
(*/*) cd=${cw:h} ;;
esac
files=( ${(ps:\0:)"$(_call_program unstaged-modified-files git ls-files --exclude-standard -m -z ${cd:+$cd})"} )
files=( ${(u)${${(M)files:#${cd}*}/(#b)(${cd}[^\/]##)*/${match[1]}}} )
_wanted 'unstaged-modified-files' expl 'unstaged modified file' compadd -f ${expl} -- "${files[@]}"
}
function __git-files_ignored {
local expl cw cd
local -a files
cw=${words[CURRENT]}
case $cw in
(*/) cd=${cw} ;;
(*/*) cd=${cw:h} ;;
esac
files=( ${(ps:\0:)"$(_call_program ignored-files git ls-files --exclude-standard -m -z ${cd:+$cd})"} )
files=( ${(u)${${(M)files:#${cd}*}/(#b)(${cd}[^\/]##)*/${match[1]}}} )
_wanted 'ignored-files' expl 'ignored file' compadd -f ${expl} -- "${files[@]}"
}
function __git-files_other {
local expl cw cd
local -a files
cw=${words[CURRENT]}
case $cw in
(*/) cd=${cw} ;;
(*/*) cd=${cw:h} ;;
esac
files=( ${(ps:\0:)"$(_call_program other-files git ls-files --exclude-standard -o -z ${cd:+$cd})"} )
files=( ${(u)${${(M)files:#${cd}*}/(#b)(${cd}[^\/]##)*/${match[1]}}} )
_wanted 'other-files' expl 'other file' compadd -f ${expl} -- "${files[@]}"
}
function __git-files_deleted {
local expl cw cd
local -a files
cw=${words[CURRENT]}
case $cw in
(*/) cd=${cw} ;;
(*/*) cd=${cw:h} ;;
esac
files=( ${(ps:\0:)"$(_call_program deleted-files git diff-index -z --diff-filter=D --name-only HEAD ${cd:+$cd})"} )
files=( ${(u)${${(M)files:#${cd}*}/(#b)(${cd}[^\/]##)*/${match[1]}}} )
_wanted 'deleted-files' expl 'deleted file' compadd -f ${expl} -- "${files[@]}"
}
# other helpers
function __git-tags {
local expl cw
local -a tags
cw=${words[CURRENT]}
tags=( ${${(f)"$(_call_program git-tags git for-each-ref --format='"%(refname)"' refs/tags/${cw:+$cw*})"}#refs/tags/} )
_wanted 'tags' expl 'git tag' compadd ${expl} -- "${tags[@]}"
}
function __git-branches {
local expl cw
local -a branches
cw=${words[CURRENT]}
branches=( ${${(f)"$(_call_program git-branches git for-each-ref --format='"%(refname)"' refs/heads/${cw:+$cw*})"}#refs/heads/} )
_wanted 'branches' expl 'git branch' compadd ${expl} -- "${branches[@]}"
}
function __git-branches_remote {
local expl cw
local -a branches
cw=${words[CURRENT]}
branches=( ${${(f)"$(_call_program git-branches-remote git for-each-ref --format='"%(refname)"' refs/remotes/${cw:+$cw*})"}#refs/remotes/} )
_wanted 'remote branches' expl 'git branch (remote)' compadd ${expl} -- "${branches[@]}"
}
function __git-remotes {
local expl cw
local -a remotes
cw=${words[CURRENT]}
remotes=( ${${${${(f)"$(_call_program git-remotes git config --get-regexp "'remote\..*\.url'")"}%% *}#remote.}%.url} )
_wanted 'remotes' expl 'remote' compadd ${expl} -- "${remotes[@]}"
}
Messages sorted by:
Reverse Date,
Date,
Thread,
Author