From: Frank Terbeck <ft@bewatermyfriend.org>
To: Nikolai Weibull <now@bitwi.se>
Cc: Felipe Contreras <felipe.contreras@gmail.com>, zsh-workers@zsh.org
Subject: Re: Slowness issue with git completion
Date: Tue, 26 Apr 2011 22:10:06 +0200 [thread overview]
Message-ID: <87ei4o7p0h.fsf@ft.bewatermyfriend.org> (raw)
In-Reply-To: <BANLkTinOA5Qm42jte6vdO4QhG2nbHEPuHA@mail.gmail.com> (Nikolai Weibull's message of "Tue, 26 Apr 2011 21:06:39 +0200")
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[@]}"
}
next prev parent reply other threads:[~2011-04-26 20:13 UTC|newest]
Thread overview: 35+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-04-26 18:26 Felipe Contreras
2011-04-26 18:43 ` Frank Terbeck
2011-04-26 19:06 ` Nikolai Weibull
2011-04-26 20:10 ` Frank Terbeck [this message]
2011-04-26 20:23 ` Felipe Contreras
2011-04-26 20:34 ` Mikael Magnusson
2011-04-26 20:57 ` Felipe Contreras
2011-04-26 20:59 ` Mikael Magnusson
2011-04-26 21:10 ` Felipe Contreras
2011-04-26 21:13 ` Mikael Magnusson
2011-04-26 21:35 ` Felipe Contreras
2011-04-26 22:03 ` Nikolai Weibull
2011-04-26 22:25 ` Felipe Contreras
2011-04-26 23:14 ` Benjamin R. Haskell
2011-04-27 7:01 ` Benjamin R. Haskell
2011-04-27 1:52 ` gi1242+zsh
2011-04-27 6:11 ` Nikolai Weibull
2011-04-27 8:30 ` Felipe Contreras
2011-04-27 8:47 ` Frank Terbeck
2011-04-27 9:06 ` Felipe Contreras
2011-04-27 10:15 ` Nikolai Weibull
2011-04-27 10:42 ` Felipe Contreras
2011-04-27 11:14 ` Nikolai Weibull
2011-04-27 4:19 ` Bart Schaefer
2011-04-27 6:13 ` Nikolai Weibull
2011-04-27 12:51 ` Nikolai Weibull
2011-04-27 14:55 ` Bart Schaefer
2011-04-27 18:36 ` Nikolai Weibull
2011-04-30 14:37 ` Simon Ruderich
2011-04-30 15:00 ` Simon Ruderich
2011-05-02 9:59 ` Nikolai Weibull
2011-05-03 13:59 ` Nikolai Weibull
2011-04-26 21:52 ` Benjamin R. Haskell
2011-04-26 22:04 ` Felipe Contreras
2011-04-26 22:35 ` Benjamin R. Haskell
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87ei4o7p0h.fsf@ft.bewatermyfriend.org \
--to=ft@bewatermyfriend.org \
--cc=felipe.contreras@gmail.com \
--cc=now@bitwi.se \
--cc=zsh-workers@zsh.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.vuxu.org/mirror/zsh/
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).