From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 14251 invoked by alias); 26 Apr 2011 20:13:13 -0000 Mailing-List: contact zsh-workers-help@zsh.org; run by ezmlm Precedence: bulk X-No-Archive: yes List-Id: Zsh Workers List List-Post: List-Help: X-Seq: 29059 Received: (qmail 24974 invoked from network); 26 Apr 2011 20:13:09 -0000 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on f.primenet.com.au X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,RCVD_IN_DNSWL_NONE, SPF_HELO_PASS autolearn=ham version=3.3.1 Received-SPF: none (ns1.primenet.com.au: domain at bewatermyfriend.org does not designate permitted sender hosts) From: Frank Terbeck To: Nikolai Weibull Cc: Felipe Contreras , zsh-workers@zsh.org Subject: Re: Slowness issue with git completion In-Reply-To: (Nikolai Weibull's message of "Tue, 26 Apr 2011 21:06:39 +0200") References: <87liyw7t0o.fsf@ft.bewatermyfriend.org> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/23.2 (gnu/linux) Date: Tue, 26 Apr 2011 22:10:06 +0200 Message-ID: <87ei4o7p0h.fsf@ft.bewatermyfriend.org> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable X-Df-Sender: 430444 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=E2=80=99t even on Cygwin, where the forking ma= kes > 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=3DM --name-only" # mod-unstaged - files, whose modifications are *not* staged # "git ls-files -m" # modified - both. # "git diff-index --diff-filter=3DM --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=3DD" # # 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_', so ie "index files" will be # completed by `__git-files_index'. function __git-files_index { local expl cw cd local -a files cw=3D${words[CURRENT]} case $cw in (*/) cd=3D${cw} ;; (*/*) cd=3D${cw:h} ;; esac files=3D( ${(ps:\0:)"$(_call_program index-files git ls-tree --name-onl= y -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=3D${words[CURRENT]} case $cw in (*/) cd=3D${cw} ;; (*/*) cd=3D${cw:h} ;; esac files=3D( ${(ps:\0:)"$(_call_program modified-files git diff-index -z -= -diff-filter=3DMDA --name-only HEAD ${cd:+$cd})"} ) files=3D( ${(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=3D${words[CURRENT]} case $cw in (*/) cd=3D${cw} ;; (*/*) cd=3D${cw:h} ;; esac files=3D( ${(ps:\0:)"$(_call_program staged-modified-files git diff-ind= ex -z --cached --diff-filter=3DMDA --name-only HEAD ${cd:+$cd})"} ) files=3D( ${(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=3D${words[CURRENT]} case $cw in (*/) cd=3D${cw} ;; (*/*) cd=3D${cw:h} ;; esac files=3D( ${(ps:\0:)"$(_call_program unstaged-modified-files git ls-fil= es --exclude-standard -m -z ${cd:+$cd})"} ) files=3D( ${(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=3D${words[CURRENT]} case $cw in (*/) cd=3D${cw} ;; (*/*) cd=3D${cw:h} ;; esac files=3D( ${(ps:\0:)"$(_call_program ignored-files git ls-files --exclu= de-standard -m -z ${cd:+$cd})"} ) files=3D( ${(u)${${(M)files:#${cd}*}/(#b)(${cd}[^\/]##)*/${match[1]}}} ) _wanted 'ignored-files' expl 'ignored file' compadd -f ${expl} -- "${fi= les[@]}" } function __git-files_other { local expl cw cd local -a files cw=3D${words[CURRENT]} case $cw in (*/) cd=3D${cw} ;; (*/*) cd=3D${cw:h} ;; esac files=3D( ${(ps:\0:)"$(_call_program other-files git ls-files --exclude= -standard -o -z ${cd:+$cd})"} ) files=3D( ${(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=3D${words[CURRENT]} case $cw in (*/) cd=3D${cw} ;; (*/*) cd=3D${cw:h} ;; esac files=3D( ${(ps:\0:)"$(_call_program deleted-files git diff-index -z --= diff-filter=3DD --name-only HEAD ${cd:+$cd})"} ) files=3D( ${(u)${${(M)files:#${cd}*}/(#b)(${cd}[^\/]##)*/${match[1]}}} ) _wanted 'deleted-files' expl 'deleted file' compadd -f ${expl} -- "${fi= les[@]}" } # other helpers function __git-tags { local expl cw local -a tags cw=3D${words[CURRENT]} tags=3D( ${${(f)"$(_call_program git-tags git for-each-ref --format=3D'= "%(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=3D${words[CURRENT]} branches=3D( ${${(f)"$(_call_program git-branches git for-each-ref --fo= rmat=3D'"%(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=3D${words[CURRENT]} branches=3D( ${${(f)"$(_call_program git-branches-remote git for-each-r= ef --format=3D'"%(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=3D${words[CURRENT]} remotes=3D( ${${${${(f)"$(_call_program git-remotes git config --get-re= gexp "'remote\..*\.url'")"}%% *}#remote.}%.url} ) _wanted 'remotes' expl 'remote' compadd ${expl} -- "${remotes[@]}" }