zsh-workers
 help / color / mirror / code / Atom feed
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[@]}"
}


  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).