zsh-users
 help / color / mirror / code / Atom feed
* git checkout and git clone completion problems
@ 2006-05-24 14:23 Hannu Koivisto
  2006-05-24 16:10 ` Nikolai Weibull
  0 siblings, 1 reply; 4+ messages in thread
From: Hannu Koivisto @ 2006-05-24 14:23 UTC (permalink / raw)
  To: Zsh Users' List

Greetings,

I'm a beginner with git so it may be that I can't interpret some
results correctly or see that they are actually repository problems
or similar.  Also, I obtained git completion code from CVS and I'm
using it, or at least trying, with Debian stable's zsh 4.2.5 so it
may be that this causes problems.

In any case, I'm experimenting with a repository cloned from
<http://sbcl.boinkor.net/git/sbcl-beta.git/>.  git is 1.3.3.

First, "git branches" gives me a list of 45 branches.  The list
looks what it is supposed to be.  Yet "git checkout <tab>" gives me
239 completions.  It seems that the list includes also tags even
though git checkout does not seem to accept tags.  Also, it seems
that for each tag there is a corresponding entry with \^\{\}
appended.  A small snippet:

resistance-is-futex
resistance-is-futex-start
resistance-is-futex-start\^\{\}

Of these only the first is a branch.  resistance-is-futex-start is
a tag.

Second, "git clone <tab>" results to:

[prompt] git clone _git-clone:10: command not found: (-q --quiet)-q[operate quietly]
         git clone <cursor position>
--local  -l  -- perform a local cloning of a repository

-- 
Hannu


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: git checkout and git clone completion problems
  2006-05-24 14:23 git checkout and git clone completion problems Hannu Koivisto
@ 2006-05-24 16:10 ` Nikolai Weibull
  2006-05-24 16:25   ` Peter Stephenson
  2006-05-24 16:29   ` Hannu Koivisto
  0 siblings, 2 replies; 4+ messages in thread
From: Nikolai Weibull @ 2006-05-24 16:10 UTC (permalink / raw)
  To: Hannu Koivisto; +Cc: Zsh Users' List

[-- Attachment #1: Type: text/plain, Size: 421 bytes --]

On 5/24/06, Hannu Koivisto <azure@iki.fi> wrote:
[git completion issues]

Sorry, here's the most up to date version.  It should definitely go
into Zsh CVS.

Here's a link to it inside the git repository in which it is being maintained:

http://git.bitwi.se/?p=dot-home.git;a=blob;h=1ec279c8d98d39f251598a7210aabcf5377dfa55;hb=f52554bf332014e6cea22b43bfbd42d99ea2ed74;f=zsh/functions/_git

Thanks.

  nikolai

[-- Attachment #2: _git --]
[-- Type: application/octet-stream, Size: 60387 bytes --]

#compdef git git-add git-am git-annotate git-apply git-applymbox git-applypatch git-archimport git-bisect git-blame git-branch git-cat-file git-check-ref-format git-checkout git-checkout-index git-cherry git-cherry-pick git-clean git-clone git-clone-pack git-commit git-commit-tree git-convert-objects git-count-objects git-cvsexportcommit git-cvsimport git-daemon git-describe git-diff git-diff-files git-diff-index git-diff-stages git-diff-tree git-fetch git-fetch-pack git-fmt-merge-msg git-format-patch git-fsck-objects git-get-tar-commit-id git-grep git-hash-object git-http-fetch git-http-push git-imap-send git-index-pack git-init-db git-local-fetch git-log git-lost-found git-ls-files git-ls-remote git-ls-tree git-mailinfo git-mailsplit git-merge git-merge-base git-merge-index git-merge-one-file git-merge-tree git-mktag git-mktree git-mv git-name-rev git-octopus git-pack-objects git-pack-redundant git-patch-id git-peek-remote git-prune git-prune-packed git-pull git-push git-read-tree git-rebase git-receive-pack git-relink git-repack git-repo-config git-request-pull git-rerere git-reset git-resolve git-rev-list git-rev-parse git-revert git-rm git-send-email git-send-pack git-shortlog git-show-branch git-show-index git-ssh-fetch git-ssh-upload git-status git-stripspace git-svnimport git-symbolic-ref git-tag git-tar-tree git-unpack-file git-unpack-objects git-update-index git-update-ref git-update-server-info git-upload-pack git-var git-verify-pack git-verify-tag git-whatchanged git-write-tree

# Commands not completed:
# git-cvsserver
# git-parse-remote 
# git-shell
# git-show
# git-sh-setup

# TODO: most commands need a valid git repository to run, so add a check for it
# so that we can make our handling a little bit cleaner (need to deal with
# GIT_DIR=... stuff as pre-command modifier)
typeset -g gitdir="${GIT_DIR:-.git}"

# TODO: suggested zstyles:
#
# zstyle ':completion::*:git-{name-rev,add,rm}:*' ignore-line true

typeset -g nul_arg=

nul_arg='-z[use NUL termination on output]'

typeset -ga abbrev_arg

abbrev_arg=(
  '--abbrev=-[show only handful hexdigits prefix]: :_guard "[[\:digit\:]]#" number'
)

typeset -ga diff_args

diff_args=(
  '--diff-filter=-[select certain kinds of files for diff]: :_guard "[ACDMRTUXB*]#" kinds'
  '--find-copies-harder[try harder to find copies]'
  '(--name-only --name-status -u -p --stat --patch-with-stat --patch-with-raw)--name-only[show only names of changed files]'
  '(--name-only --name-status -u -p --stat --patch-with-stat --patch-with-raw)--name-status[show only names and status of changed files]'
  '(--name-only --name-status -u -p --stat --patch-with-stat --patch-with-raw)'{-u,-p}'[generate diff in patch format]'
  '(--name-only --name-status -u -p --stat --patch-with-stat --patch-with-raw)--stat[generate a diffstat instead of a patch]' \
  '(--name-only --name-status -u -p --stat --patch-with-stat --patch-with-raw)--patch-with-stat[generate patch and prepend its diffstat]' \
  '(--name-only --name-status -u -p --stat --patch-with-stat --patch-with-raw)--patch-with-raw[generate patch but keep also the default raw diff output]' \
  '-B-[break complete rewrite changes into pairs of given size]: :_guard "[[\:digit\:]]#" size'
  '-C-[detect copies as well as renames with given scope]: :_guard "[[\:digit\:]]#" size'
  '-l-[limit number of rename/copy targets to run]: :_guard "[[\:digit\:]]#" number'
  '-M-[detect renames with given scope]: :_guard "[[\:digit\:]]#" size'
  '-O-[output patch in the order of glob-pattern lines in given file]:file:_files'
  '-R[do a reverse diff]'
  '-S-[look for differences that contain the given string]:string'
  '--pickaxe-all[when -S finds a change, show all changes in that changeset]'
  '--pickaxe-regex[treat argument of -S as regular expression]'
  '--full-index[show full object name of pre- and post-image blob]'
  $abbrev_arg \
  $nul_arg
)

typeset -g pretty_arg=
pretty_arg='--pretty=-[pretty print commit messages]::pretty print:((raw\:"the raw commits"
                                                                     medium\:"most parts of the messages"
                                                                     short\:"few headers and only subject of messages"
                                                                     full\:"all parts of the commit messages"
                                                                     oneline\:"commit-ids and subject of messages"))'

typeset -g exec_arg=
exec_arg='--exec=-[specify path to git-upload-pack on remote side]:remote path'

typeset -ga fetch_args

fetch_args=(
  '-a[fetch all objects]'
  '-c[fetch commit objects]'
  '--recover[recover from a failed fetch]'
  '-t[fetch trees associated with commit objects]'
  '-v[show what is downloaded]'
  '-w[write commit-id into the filename under "$GIT_DIR/refs/<filename>"]:filename'
)

typeset -ga merge_args

merge_args=(
  '(-n --no-summary)'{-n,--no-summary}'[do not show diffstat at the end of the merge]'
  '--no-commit[perform the merge but do not autocommit]'
)

typeset -ga merge_strategy

merge_strategy=(
  '(-s --strategy)'{-s,--strategy=}'[use given merge strategy]:strategy:(recursive octopus resolve stupid ours)'
)

typeset -ga force_ref_arg

force_ref_arg=('(-f --force)'{-f,--force}'[allow refs that are not ancestors to be updated]')

typeset -ga tags_fetch_arg

tags_fetch_arg=(
  '(--no-tags -t --tags)--no-tags[disable automatic tag following]'
  '(--no-tags -t --tags)'{-t,--tags}'[fetch remote tags]'
)

typeset -g author_conversion_file_arg=
author_conversion_file_arg='-A[specify author-conversion file]:author-conversion file:_files'

typeset -g verbose_arg=
verbose_arg='-v[produce verbose output]'

typeset -g help_arg=
help_arg='-h[display usage information]'

typeset -ga common_fetch_args

common_fetch_args=(
  '(-a --append)'{-a,--append}'[append ref names and object names of fetched refs to "$GIT_DIR/FETCH_HEAD"]'
  '--upload-pack[pass --exec=<upload-pack> to git-fetch-pack]:remote pack'
  $force_ref_arg
  $tags_fetch_arg
  '(-k --keep)'{-k,--keep}'[keep downloaded pack]'
  '(-u --update-head-ok)'{-u,--update-head-ok}'[allow updates of current branch head]'
)

typeset -ga whitespace_arg

whitespace_arg=(
  '--whitespace=-[detect a new or modified line that ends with trailing whitespaces]:whitespace:((nowarn\:"turn off the trailing whitespace warning"
												  warn\:"output warning, but apply patch"
												  error\:"output warning and refuse to apply patch"
												  error-all\:"similar to error but show all errors"
												  strip\:"output warning and strip trailing whitespace"))'
)

# TODO: either skip uninteresting commands or skip the description - the list
# is just too long
_git_commands () {
  local -a commands

  commands=(
    'add:add paths to the index'
    'am:apply patches from a mailbox (cooler than applymbox)'
    'annotate:annotate file lines with commit info'
    'apply:apply patch on a git index file and a work tree'
    'applymbox:apply patches from a mailbox'
    'applypatch:apply one patch extracted from an e-mail'
    'archimport:import an Arch repository into git'
    'bisect:find the change that introduced a bug'
    'blame:blame file lines on commits'
    'branch:create and show branches'
    'cat-file:provide content or type information for repository objects'
    'check-ref-format:makes sure that a reference-name is well formed'
    'checkout:checkout and switch to a branch'
    'checkout-index:copy files from the index to the working directory'
    'cherry:find commits not merged upstream'
    'cherry-pick:cherry-pick the effect of an existing commit'
    'clean:remove untracked files from the working tree'
    'clone:clone a repository into a new directory'
    'clone-pack:clone a repository into the current repository (transport)'
    'commit:record changes to the repository'
    'commit-tree:create a new commit object'
    'convert-objects:convert old-style git repository'
    'count-objects:count unpacked objects and display their disk consumption'
    'cvsexportcommit:export a commit to a CVS checkout'
    'cvsimport:import a CVS "repository" into a git repository'
    'daemon:start a really simple server for git repositories'
    'describe:show the most recent tag that is reachable from a commit'
    'diff:show changes between commits, commit and working tree, etc.'
    'diff-files:compare files in the working tree and the index'
    'diff-index:compare content and mode of blobs between index and repository'
    'diff-stages:compare two "merge states" in the index file'
    'diff-tree:compare the content and mode of blobs found via two tree objects'
    'fetch:download objects and a head from another repository'
    'fetch-pack:receive missing objects from another repository'
    'format-patch:prepare patches for e-mail submission'
    'fmt-merge-msg:produce merge commit message'
    'fsck-objects:verify the connectivity and validity of the objects in the database'
    'get-tar-commit-id:extract commit ID from an archive created using tar-tree'
    'grep:print lines matching a pattern'
    'hash-object:compute object ID from a file'
    'http-fetch:download remote git repository via HTTP'
    'http-push:push missing objects using HTTP/DAV'
    'imap-send:dump mailbox from stdin into imap folder'
    'index-pack:build pack index file for an existing packed archive'
    'init-db:create empty git object database'
    'local-fetch:duplicate another git repository on a local system'
    'log:show commit logs'
    'lost-found:recover lost references that luckily have not yet been pruned'
    'ls-files:information about files in the index/working directory'
    'ls-remote:show references in a remote or local repository'
    'ls-tree:display tree object in human-readable form'
    'mailinfo:extract patch from a single e-mail message'
    'mailsplit:split mbox file into a list of files'
    'merge:grand unified merge driver'
    'merge-base:find as good a common ancestor as possible for a merge'
    'merge-index:run merge for files needing merging'
    'merge-one-file:standard helper-program to use with merge-index'
    'merge-tree:show three-way merge without touching index'
    'mktag:create tag object'
    'mktree:build tree-object from ls-tree formatted text'
    'mv:move or rename file, directory, or symlink'
    'name-rev:find symbolic names for given revisions'
    'octopus:merge more than two commits'
    'pack-objects:create packed archive of objects'
    'pack-redundant:find redundant pack files'
    'parse-remote:routines to help parsing $GIT_DIR/remotes/'
    'patch-id:compute unique ID for a apatch'
    'peek-remote:list references on a remote repository using the upload-pack protocol'
    'prune:prune all unreachable objects from the object database'
    'prune-packed:remove extra objects that are already in pack files'
    'pull:fetch from and merge with a remote repository'
    'push:update remote refs along with associated objects'
    'read-tree:read tree information into the directory index'
    'rebase:rebase local commits to new upstream head'
    'receive-pack:command invoked by send-pack to receive what is pushed to it'
    'relink:hardlink common objects in local repositories'
    'repack:pack unpacked objects in a repository'
    'repo-config:get and set options in "$GIT_DIR/config"'
    'request-pull:generate summary of pending changes'
    'rerere:reuse recorded resolve'
    'reset:reset current HEAD to the specified state'
    'resolve:merge two commits'
    'rev-list:list commit object in reverse chronological order'
    'rev-parse:pick out and massage parameters for other git commands'
    'revert:revert existing commit'
    'rm:remove files from the working tree and from the index'
    'send-email:send patch-e-mails out of "format-patch" output'
    'send-pack:push to remote repository, intelligently'
    'shortlog:summarize git log output'
    'show-branch:show branches and their commits'
    'show-index:display contents of a pack idx file'
    'ssh-fetch:pull from remote repository over an SSH connection'
    'ssh-upload:"server-side" helper program used by ssh-fetch'
    "status:show working-tree's status"
    'stripspace:filter out empty lines'
    'svnimport:import SVN repository into git'
    'symbolic-ref:read and modify symbolic references'
    'tag:create tag object signed with GPG'
    'tar-tree:create tar archive of the files in the named tree'
    "unpack-file:create temporary file with blob's contents"
    'unpack-objects:unpack objects out of packed archive'
    'update-index:modify index in some given way'
    'update-ref:update object name stored in a reference safely'
    'update-server-info:update auxiliary information on a dumb server'
    'upload-pack:command invoked by clone-pack and fetch-pack'
    'var:display git logical variable'
    'verify-pack:validate packed git archive files'
    'verify-tag:check GPG signature of a tag'
    'whatchanged:show commit-logs and differences they introduce'
    'write-tree:create tree from the current index')

  _describe -t commands 'git command' commands && ret=0
}

# TODO: this needs to be cleaned up and fixed
local curcontext=$curcontext ret=1

#if [[ $words[1] == git ]]; then
#  if (( CURRENT == 2 )); then
#    _git_commands
#  else
#    shift words
#    (( CURRENT-- ))
#    curcontext="${curcontext%:*:*}:git-$words[1]:"
#    _call_function ret _git-$words[1]
#  fi
#else
#  _call_function ret _$words[1]
#fi

if [[ $words[1] == git ]]; then
  local context state line
  typeset -A opt_args
  _arguments \
    '(- :)--version[display version information]' \
    '(- :)--help[display help message]' \
    '--exec-path=-[path containing core git-programs]::directory:_directories' \
    ':command:->command' \
    '*::options:->options' && ret=0
  case $state in
    (command)
      _git_commands
      ;;
    (options)
      curcontext="${curcontext%:*:*}:git-$words[1]:"
      _call_function ret _git-$words[1]
      ;;
  esac
else
  _call_function ret _$words[1]
fi

_git-annotate () {
  _arguments -S \
    '(-l --long)'{-l,--long}'[show long rev]' \
    '(-t --time)'{-t,--time}'[show raw timestamp]' \
    '(-r --rename)'{-r,--rename}'[follow renames]' \
    '(-S --rev-file)'{-S,--rev-file=}'[use revs from revs-file]:revs-file:_files' \
    '(-h --help)'{-h,--help}'[show help message]' \
    ':file:__git_cached_files' \
    '::revision:__git_revisions' && ret=0
}

_git-apply () {
  _arguments \
    $nul_arg \
    '--allow-binary-replacement[allow binary files to be patched]' \
    '--apply[apply patches that would otherwise not be applied]' \
    '--check[check if patches are applicable (turns off "apply")]' \
    '--exclude=-[skip files matching specified pattern]:pattern' \
    '--index[make sure that the patch is applicable to the index]' \
    '--index-info[output information about original version of a blob if available]' \
    '--no-add[ignore additions made by the patch]' \
    '--numstat[same as --stat but in decimal notation and complete pathnames (turns off "apply")]' \
    '--stat[output diffstat for the input (turns off "apply")]' \
    '--summary[output summary of git-diff extended headers (turns off "apply")]' \
    '-p-[remove N leading slashes from traditional diff paths]: :_guard  "[[\:digit\:]]#" number' \
    '-C-[ensure at least N lines of context match before and after each change]: :_guard  "[[\:digit\:]]#" number' \
    $whitespace_arg \
    '*::patch:_files' && ret=0
}

_git-blame () {
  _arguments -S \
    '(-c --compability)'{-c,--compability}'[use the same output mode as git-annotate]' \
    '(-l --long)'{-l,--long}'[show long rev]' \
    '-S[use revs from revs-file]:revs-file:_files' \
    '(-h --help)'{-h,--help}'[show help message]' \
    ':file:__git_cached_files' \
    '::revision:__git_revisions' && ret=0
}

_git-checkout-index () {
  _arguments -S \
    $nul_arg \
    '(-a --all :)'{-a,--all}'[check out all files in the index]' \
    '(-f --force)'{-f,--force}'[force overwrite of existing files]' \
    '(-n --no-create)'{-n,--no-create}'[do not checkout new files]' \
    '--stage=-[check out files from named stage]:stage:(1 2 3 all)' \
    '--stdin[read list of paths from the standard input]' \
    '--temp[write the content to temporary files]' \
    '--prefix=-[prefix to use when creating files]:directory:_directories' \
    '(-q --quiet)'{-q,--quiet}'[do not complain about existing files or missing files]' \
    '(-u --index)'{-u,--index}'[update stat information in index]' \
    '*::file:__git_cached_files' && ret=0
}

_git-clean () {
  _arguments \
    '-d[remove untracked directories]' \
    '-n[just show what would be done]' \
    '-q[be quiet, only report errors]' \
    '(-X -x)-x[do use ignore rules]' \
    '(-X -x)-X[remove only files ignored by git]' && ret=0
}

_git-commit-tree () {
  if (( CURRENT == 2 )); then
    __git_trees && ret=0
  elif [[ $words[CURRENT-1] == -p ]]; then
    local expl
    _description commits expl 'parent commit'
    __git_objects $expl && ret=0
  else
    compadd - '-p'
  fi
}

_git-hash-object () {
  _arguments \
    '(:)--stdin[read object from standard input]' \
    '-t[the type of object to create]:object type:((blob\:"a blob of data"
                                                    commit\:"a tree with parent commits"
                                                    tag\:"a symbolic name for another object"
                                                    tree\:"a recursive tree of blobs"))' \
    '-w[write the object to the object database]' \
    '(--stdin):file:_files' && ret=0
}

_git-index-pack () {
  _arguments \
    '-o[write generated pack index into specified file]:file:_files' \
    ':pack file:_files -g "*.pack"' && ret=0
}

_git-init-db () {
  _arguments \
    '--shared[share repository amongst several users]' \
    '--template=-[directory to use as a template for the object database]:directory:_directories' && ret=0
}

_git-merge-index () {
  if (( CURRENT > 2 )) && [[ $words[CURRENT-1] != -[oq] ]]; then
    _arguments -S \
      '(:)-a[run merge against all files in the index that need merging]' \
      '*:index file:__git_cached_files' && ret=0
  else
    typeset -a arguments

    (( CURRENT == 2 )) && arguments+='-o[skip failed merges]'
    (( CURRENT == 2 || CURRENT == 3 )) && arguments+='(-o)-q[do not complain about failed merges]'
    (( 2 <= CURRENT && CURRENT <= 4 )) && arguments+='*:merge program:_files -g "*(*)"'

    _arguments -S $arguments && ret=0
  fi
}

_git-merge-tree () {
  _arguments \
    ':base-tree:__git_tree_ishs' \
    ':branch 1:__git_tree_ishs' \
    ':branch 2:__git_tree_ishs' && ret=0
}

_git-mktag () {
  _message 'no arguments allowed; only accepts tags on standard input'
}

_git-mktree () {
  _arguments \
    $nul_arg && ret=0
}

_git-pack-objects () {
  _arguments \
    '--depth=-[maximum delta depth]: :_guard "[[\:digit\:]]#" number' \
    '--incremental[ignore objects that have already been packed]' \
    '--no-reuse-delta[do not reuse existing deltas, but compute them from scratch]' \
    '--non-empty[only create a package if it contains at least one object]' \
    '--local[similar to --incremental, but only ignore unpacked non-local objects]' \
    '(:)--stdout[write the pack to standard output]' \
    '-q[do not report progress]' \
    '--window=-[number of objects to use per delta compression]: :_guard "[[\:digit\:]]#" number' \
    '(--stdout):base-name:_files' && ret=0
}

_git-prune-packed () {
  _arguments \
    '-n[only list the objects that would be removed]' && ret=0
}

_git-read-tree () {
  if (( CURRENT == 2 )); then
    _arguments \
      '--reset[perform a merge, not just a read, ignoring unmerged entries]' \
      '--trivial[only perform trivial merges]' \
      '-m[perform a merge, not just a read]' \
      ':tree-ish:__git_tree_ishs' && ret=0
  elif [[ $words[2] == (-m|--reset) ]]; then
    _arguments \
      '(--reset)--aggressive[resolve a few more merge cases]' \
      '(-u)-i[update only the index; ignore changes in work tree]' \
      '(-i)-u[update the work tree after successful merge]' \
      '2:first tree-ish to be read/merged:__git_tree_ishs' \
      '3:second tree-ish to be read/merged:__git_tree_ishs' \
      '4:third tree-ish to be read/merged:__git_tree_ishs' && ret=0
  else
    _message 'no more arguments'
  fi
}

_git-repo-config () {
  local name_arg

  if (( $words[(I)--get-regexp] )); then
    name_arg=':name regex'
  else
    name_arg=':name:__git_repo-config_name'
  fi

  _arguments \
    '(      --bool)--int[setting is an integer]' \
    '(--int       )--bool[setting is a boolean]' \
    '(-)--replace-all[replace all values of the given key]' \
    '(- 2)--get[get the value of the given key]' \
    '(- 2)--get-all[get all values of the given key]' \
    '(- 2)--get-regexp[like "--get-all", but interpret "name" as regular expression]' \
    '(- 2)--unset[remove the given key]' \
    '(- 2)--unset-all[remove all values of the key]' \
    $name_arg \
    '::value' \
    '::value regex' && ret=0
}

_git-unpack-objects () {
  _arguments \
    '-n[only list the objects that would be unpacked]' \
    '-q[run quietly]' && ret=0
}

_git-update-index () {
  local -a refreshables

  if (( $words[(I)--refresh] )); then
    refreshables=(
      '--ignore-missing[ignore missing files when refreshing the index]'
      '--unmerged[if unmerged changes exists, ignore them instead of exiting]'
      '-q[run quietly]')
  fi

  _arguments -S \
    $refreshables \
    '--add[add files not already in the index]' \
    '(--no-assume-unchanged)--assume-unchanged[set the "assume unchanged" bit for the given paths]' \
    '*--cacheinfo[insert information directly into the cache]: :_guard "[0-7]#" "octal file mode": :_guard "[[\:xdigit\:]]#" "object id":file:_files' \
    '--chmod=-[set the execute permissions on the updated files]:permission:((-x\:executable +x\:"not executable"))' \
    '(--remove)--force-remove[remove files from both work tree and the index]' \
    '--index-info[read index information from stdin.]' \
    '--info-only[only insert files object-IDs into index]' \
    '(--assume-unchanged)--no-assume-unchanged[unset the "assume unchanged" bit for the given paths]' \
    '--refresh[refresh the index]' \
    '(--force-remove)--remove[remove files that are in the index but are missing from the work tree]' \
    '--replace[replace files already in the index if necessary]' \
    '--stdin[read list of paths from standard input]' \
    '--unresolve[restore "unmerged" or "needs updating" state of files]' \
    '--verbose[report what is being added and removed from the index]' \
    '-z[paths are separated with NUL instead of LF for --stdin]' \
    '*::file:_files' && ret=0
}

_git-write-tree () {
  _arguments \
    '--missing-ok[ignore objects in the index that are missing in the object database]' && ret=0
}

_git-cat-file () {
  if (( CURRENT == 2 )); then
    _arguments \
      '-e[exit with zero status if object exists]' \
      '-s[show the size of the given object]' \
      '-t[show the type of the given object]' \
      '-p[pretty-print the given object]' \
      ':object type:(blob commit tag tree)' && ret=0
  elif (( CURRENT == 3 )); then
    __git_objects && ret=0
  else
    _message 'no more arguments'
  fi
}

_git-describe () {
  _arguments \
    $abbrev_arg \
    '--all[use any ref found in "$GIT_DIR/refs/"]' \
    '--tags[use any tag found in "$GIT_DIR/refs/tags/"]' \
    '*:committish:__git_commits' && ret=0
}

_git-diff-index () {
  _arguments -S \
    $diff_args \
    '--cached[do not consider the work tree at all]' \
    '-m[flag non-checked-out files as up-to-date]' \
    ':tree-ish:__git_tree_ishs' \
    '*::index file:__git_cached_files' && ret=0
}

_git-diff-files () {
  _arguments \
    $diff_args \
    '(-0 -1 -2 -3 --base --ours --theirs -c --cc)-0[omit diff output for unmerged entries]' \
    '(-0 -1 -2 -3 --base --ours --theirs -c --cc)'{-1,--base}'[diff against "base" version]' \
    '(-0 -1 -2 -3 --base --ours --theirs -c --cc)'{-2,--ours}'[diff against "our branch" version]' \
    '(-0 -1 -2 -3 --base --ours --theirs -c --cc)'{-3,--theirs}'[diff against "their branch" version]' \
    '(-0 -1 -2 -3 --base --ours --theirs -c --cc)'{-c,--cc}'[compare "our branch", "their branch" and working tree files]' \
    '-q[remain silent even on nonexisting files]' \
    '*::file:_files' && ret=0
}

_git-diff-stages () {
  _arguments \
    $diff_args \
    ':stage 1:__git_stages' \
    ':stage 2:__git_stages' \
    '*::index file:_files' && ret=0
}

_git-diff-tree () {
  local curcontext=$curcontext state line
  typeset -A opt_args

  _arguments -S \
    $diff_args \
    $pretty_arg \
    '--no-commit-id[skip output of commit IDs]' \
    '--root[show diff against the empty tree]' \
    '--stdin[read commit and tree information from standard input]' \
    '-m[show merge commits]' \
    '(-t)-r[recurse into subdirectories]' \
    '-t[show tree entry itself as well as subtrees (implies -r)]' \
    '-s[do not show differences]' \
    '-v[show commit message before the differences]' \
    '(-c --cc)-c[show differences from each of the parents to the merge result]' \
    '(-c --cc)--cc[how differences from each of the parents and omit differences from only one parent]' \
    '--always[always show commit itself and the commit log message]' \
    ':tree-ish:__git_tree_ishs' \
    '*::file:->files' && ret=0

  case $state in
    files)
      if (( $#line > 2 )); then
        # TODO: this is probably just stupid to do.
        # What'd be nice would be
        # common files:
        #   ...
        # original tree:
        #   ...
        # new tree:
        #   ...
        _alternative \
          "original tree:original tree:__git_tree_files $line[1]" \
          "new tree:new tree:__git_tree_files $line[2]" && ret=0
      else
        _alternative \
          ': :__git_tree_ishs' \
          ": :__git_tree_files $line[1]" && ret=0
      fi
      ;;
  esac
}

_git-fmt-merge-msg () {
  _message "you probably should not be issuing this command"
}

_git-fsck-objects () {
  _arguments -S \
    '--cache[consider objects recorded in the index as head nodes for reachability traces]' \
    '--full[check all object directories]' \
    '--root[show root nodes]' \
    '--strict[do strict checking]' \
    '--tags[show tags]' \
    '--unreachable[show objects that are unreferenced in the object database]' \
    '*::object:__git_objects' && ret=0
}

_git-ls-files () {
  _arguments -S \
    $nul_arg \
    '(-c --cached)'{-c,--cached}'[show cached files in the output]' \
    '(-d --deleted)'{-d,--deleted}'[show deleted files in the output]' \
    '(-i --ignored)'{-i,--ignored}'[show ignored files in the output]' \
    '(-k --killed)'{-k,--killed}'[show killed files in the output]' \
    '(-m --modified)'{-m,--modified}'[show modified files in the output]' \
    '(-o --others)'{-o,--others}'[show other files in the output]' \
    '(-s --stage)'{-s,--stage}'[show stage files in the output]' \
    '(-t -v)-t[identify each files status (HMRCK?)]' \
    '(-u --unmerged)'{-u,--unmerged}'[show unmerged files in the output]' \
    '(-t -v)-v[identify each files status (hmrck?)]' \
    '*'{-x,--exclude=-}'[skip files matching given pattern]:file pattern' \
    '*'{-X,--exclude-from=-}'[skip files matching patterns in given file]:file:_files' \
    '*--exclude-per-directory=-[skip directories matching patterns in given file]:file:_files' \
    '--directory[if a whole directory is classified as "other", show just its name]' \
    '--error-unmatch[if any <file> does not appear in the index, treat this as an error]' \
    '--full-name[force paths to be output relative to the project top directory]' \
    '*::index file:_files' && ret=0
}

_git-ls-tree () {
  local curcontext=$curcontext state line
  typeset -A opt_args

  _arguments \
    $nul_arg \
    $abbrev_arg \
    '(-t)-d[do not show children of given tree (implies -t)]' \
    {--name-only,--name-status}'[list only filenames, one per line]' \
    '-r[recurse into subdirectories]' \
    '-t[show tree entries even when going to recurse them]' \
    ':tree-ish:__git_tree_ishs' \
    '*::tree file:->files' && ret=0

  case $state in
    files)
      __git_tree_files $line[1] && ret=0
      ;;
  esac
}

_git-imap-send () {
_message 'no arguments allowed; accepts mailbox file on standard input'
}

_git-merge-base () {
  _arguments \
    '(-a --all)'{-a,--all}'[show all common ancestors]' \
    ':commit 1:__git_commits' \
    ':commit 2:__git_commits' && ret=0
}

_git-name-rev () {
  _arguments -S \
    '--tags[only use tags to name the commits]' \
    '(--stdin :)--all[list all commits reachable from all refs]' \
    '(--all :)--stdin[read from stdin and append revision-name]' \
    '(--stdin --all)*:commit-ish:__git_revisions' && ret=0
}

_git-pack-redundant () {
  _arguments \
    '(:)--all[process all packs]' \
    '--alt-odb[do not require objects to be present in local packs]' \
    '--verbose[output some statistics to stderr]' \
    '(--all)*::packs:_files -g "*.pack"' && ret=0
}

# TODO: lots of option undocumented (revision.c)
_git-rev-list () {
  if (( $words[(I)--] && $words[(I)--] != CURRENT )); then
    _arguments \
      '*:index file:__git_cached_files' && ret=0
  else
    local show_breaks unpacked

    (( $words[(I)--merge-order] )) && show_breaks='--show-breaks[show commit prefixes]'
    (( $words[(I)--objects(|-edge)] )) && unpacked='--unpacked[print object IDs that are not in packs]'
    _arguments -S \
      '--all[show all commits from refs]' \
      '--bisect[show only the middlemost commit object]' \
      '(--sparse)--dense[this is the inverse of --sparse, and is also the default]' \
      '(--pretty)--header[show commit headers]' \
      '(--objects --objects-edge)--objects[show object ids of objects referenced by the listed commits]' \
      '(--objects --objects-edge)--objects-edge[show object ids of objects referenced by the listed and excluded commits]' \
      '--max-age[maximum age of commits to output]: :_guard "[[\:digit\:]]#" timestamp' \
      '--max-count[maximum number of commits to output]: :_guard "[[\:digit\:]]#" number' \
      '(--topo-order)--merge-order[decompose into minimal and maximal epochs]' \
      '--min-age[minimum age of commits to output]: :_guard "[[\:digit\:]]#" timestamp' \
      '--parents[show parent commits]' \
      '(--header)'$pretty_arg \
      '(--dense)--sparse[when paths are given, output only commits that changes any of them]' \
      '(--merge-order)--topo-order[show commits in topological order]' \
      $show_breaks \
      $unpacked \
      '*:commit id:__git_commits2' && ret=0
  fi
}

_git-show-index () {
  _message 'no arguments allowed; accepts index file on standard input'
}

_git-tar-tree () {
  _arguments \
    ':tree-ish:__git_tree_ishs' \
    ':base:_files' && ret=0
}

_git-unpack-file () {
  _arguments \
    ':blob id:__git_blobs' && ret=0
}

_git-var () {
  _arguments \
    '(:)-l[show logical variables]' \
    '(-):variable:((GIT_AUTHOR_IDENT\:"name and email of the author" \
                    GIT_COMMITTER_IDENT\:"name and email of committer"))' && ret=0
}

_git-verify-pack () {
  _arguments -S \
    '-v[show objects contained in pack]' \
    '*:index file:_files -g "*.idx"' && ret=0
}
 
_git-clone-pack () {
  _arguments \
    $exec_arg \
    ':repository:__git_any_repositories' \
    '*:head:__git_heads' && ret=0
}

_git-fetch-pack () {
  _arguments \
    $exec_arg \
    '-k[do not invoke "git-unpack-objects" on received data]'
    '-q[pass "-q" flag to "git-unpack-objects"]' \
    ':repository:__git_any_repositories' \
    '*:head:__git_heads' && ret=0
}

_git-http-fetch () {
  _arguments \
    $fetch_args \
    ':commit id:__git_commits' \
    ':URL:_urls' && ret=0
}

_git-http-push () {
  _arguments \
    '--complete[do not assume that the remote repository is complete]' \
    $force_ref_arg \
    '--verbose[report the list of objects being walked locally and sent to the remote]' \
    ':URL:_urls' \
    '*:remote refs' && ret=0
}

_git-local-fetch () {
  _arguments \
    $fetch_args \
    '-l[hard-link objects]' \
    '-n[do not copy objects]' \
    '-s[sym-link objects]' \
    ':commit id:__git_commits' \
    ':directory:_directories' && ret=0
}

_git-peek-remote () {
  _arguments \
    $exec_arg \
    ':remote repository:__git_remote_repository' && ret=0
}

_git-receive-pack () {
  _message "you probably should not be issuing this command"
}

_git-send-pack () {
  _arguments \
    $exec_arg \
    '--all[update all refs that exist locally]' \
    '--force[update remote orphaned refs]' \
    ':repository:__git_any_repositories' \
    '*:remote refs' && ret=0
}

_git-ssh-fetch () {
  _arguments \
    $fetch_args \
    ':commit id:__git_commits' \
    ':URL:_urls' && ret=0
}

_git-ssh-upload () {
  _arguments \
    $fetch_args \
    ':commit id:__git_commits' \
    ':URL:_urls' && ret=0
}

_git-update-server-info () {
  _arguments \
    '(-f --force)'{-f,--force}'[update the info files from scratch]' && ret=0
}

_git-upload-pack () {
  _arguments \
    ':directory:_directories' && ret=0
}

_git-add () {
  _arguments -S \
    '-n[do not actually add files; only show which ones would be added]' \
    '-v[show files as they are added]' \
    '*:file:__git_other_files' && ret=0
}

_git-am () {
  _arguments \
    '--3way[use 3-way merge if patch does not apply cleanly]' \
    '--binary[pass "--allow-binary-replacement" to "git-apply"]' \
    '--dotest=-[use given directory as working area instead of .dotest]:directory:_directories' \
    '--interactive[apply patches interactively]' \
    '--keep[pass "-k" flag to "git-mailinfo"]' \
    '--resolved[continue after resolving patch failure by hand]' \
    '--signoff[add "Signed-off-by:" line to the commit message]' \
    '--skip[skip the current patch]' \
    '--utf8[pass "-u" flag to "git-mailinfo"]' \
    $whitespace_arg \
    '*:mbox file:_files' && ret=0
}

_git-applymbox () {
  _arguments \
    '-k[do not modify "Subject:" header]' \
    '-m[apply patches with "git-apply" and fail if patch is unclean]' \
    '-q[apply patches interactively]' \
    '-u[encode commit information in UTF-8]' \
    '(1)-c[restart command after fixing an unclean patch]:patch:_files -g ".dotest/0*"' \
    '(-c):mbox file:_files' \
    '::signoff file:__git_signoff_file' && ret=0
}

_git-bisect () {
  local bisect_cmds

  bisect_cmds=(
    bad:"mark current or given revision as bad"
    good:"mark current or given revision as good"
    log:"show the log of the current bisection"
    next:"find next bisection to test and check it out"
    replay:"replay a bisection log"
    reset:"finish bisection search and return to the given branch (or master)"
    start:"reset bisection state and start a new bisection"
    visualize:"show the remaining revisions in gitk"
  )

  if (( CURRENT == 2 )); then
    _describe -t command "git-bisect commands" bisect_cmds && ret=0
  else
    case $words[2] in
      (bad)
        _arguments \
          '2:revision:__git_commits' && ret=0
        ;;
      (good)
        _arguments \
          '*:revision:__git_commits' && ret=0
        ;;
      (replay)
        _arguments \
          '2:file:_files' && ret=0
        ;;
      (reset)
        _arguments \
          '2:branch:__git_heads' && ret=0
        ;;
      (*)
        _nothing
        ;;
    esac
  fi
}

# TODO: complete branch names?
_git-branch () {
  _arguments \
    '(-D -d -f :)-r[list only the "remote" branches]' \
    '(-D -d -f -r 2)-d[delete a branch, which must be fully merged]' \
    '(-D -d -f -r 2)-D[delete a branch]' \
    '(-D -d -f -r)-f[force a reset of <branch-name> to <start-point>]' \
    ':branch-name' \
    '::start-point:__git_revisions' && ret=0
}

# FIXME:
# 'git-checkout' [-f] [-b <new_branch>] [-m] [<branch>]
# 'git-checkout' [-m] [<branch>] <paths>...
_git-checkout () {
  _arguments \
    '-b[create a new branch based at given branch]:branch-name' \
    '-f[force a complete re-read]' \
    '-m[3way merge current branch, working tree and new branch]' \
    '::branch:__git_revisions' \
    '(-b -f)*:file:__git_cached_files' && ret=0
}

_git-cherry-pick () {
  _arguments \
    '(-e --edit)'{-e,--edit}'[edit commit before committing the revert]' \
    '(-n --no-commit)'{-n,--no-commit}'[do not make the actually commit]' \
    '(-r --replay)'{-r,--replay}'[use original commit message intact]' \
    ':commit:__git_revisions' && ret=0
}

# TODO: complete branch names?
_git-clone () {
  local -a shared

  if (( $words[(I)(-l|--local)] )); then
    shared=('(-s --shared)'{-s,--shared}'[share the objects with the source repository]')
  fi

  _arguments \
    '--bare[make a "bare" GIT repository]' \
    '(-l --local)'{-l,--local}'[perform a local cloning of a repository]' \
    $shared \
    '--reference[reference repository]:repository:_directories' \
    '(-q --quiet)'{-q,--quiet}'[operate quietly]' \
    '-n[do not checkout HEAD after clone is complete]' \
    '-o[use given name instead of "origin" as branch name]:name' \
    '(-u --upload-pack)'{-u,--uploadpack}'[specify path to git-upload-pack on remote side]:remote path' \
    ':repository:__git_any_repositories' \
    '*:directory:_directories' && ret=0
}

_git-commit () {
  _arguments -S \
    '(-a --all)'{-a,--all}'[update all paths in the index file]' \
    '--amend[amend the tip of the current branch]' \
    '--author[override the author name used in the commit]:author name' \
    '(-e --edit)'{-e,--edit}'[edit the commit message before committing]' \
    '(-o --only -i --include)'{-i,--include}'[update the given files and commit the whole index]' \
    '(-o --only -i --include)'{-o,--only}'[commit only the given files]' \
    '(-n --no-verify -v --verify)'{-n,--no-verify}'[do not look for suspicious lines the commit introduces]' \
    '(-s --signoff)'{-s,--signoff}'[add Signed-off-by line at the end of the commit message]' \
    '(-v --verify -n --no-verify)'{-v,--verify}'[look for suspicious lines the commit introduces]' \
    '*:file:__git_modified_files' \
    - '(message)' \
      '(-c -C --reedit-message --reuse-message)'{-c,--reedit-message=}'[use existing commit object and edit log message]:commit id:__git_commits' \
      '(-c -C --reedit-message --reuse-message)'{-C,--reuse-message=}'[use existing commit object with same log message]:commit id:__git_commits' \
      '(-F --file)'{-F,--file=}'[read commit message from given file]:file:_files' \
      '(-m --message)'{-m,--message=}'[use the given message as the commit message]:message' && ret=0
}

# TODO: __git_files should be __git_tree_files (do like in git-diff-tree and
# such)
_git-diff () {
  _arguments \
    $diff_args \
    '::original revision:__git_revisions' \
    '::new revision:__git_revisions' \
    '*::index file:__git_cached_files' && ret=0
}

_git-fetch () {
  _arguments \
    $common_fetch_args \
    ':repository:__git_any_repositories' \
    '*:refspec:__git_ref_specs' && ret=0
}

# TODO: should support R1..R2 syntax
_git-format-patch () {
  _arguments \
    $diff_args \
    '(-a --author)'{-a,--author}'[output From: header for your own commits as well]' \
    '(-c --check)'{-c,--check}'[display suspicious lines in the patch]' \
    '(-d --date)'{-d,--date}'[output Date: header for your own commits as well]' \
    '(-h --help)'{-h,--help}'[display usage information]' \
    '(-k --keep-subject)'{-k,--keep-subject}'[do not strip/add \[PATCH\] from the first line of the commit message]' \
    '(-m --mbox)'{-m,--mbox}'[use true mbox formatted output]' \
    '(-n --numbered)'{-n,--numbered}'[name output in \[PATCH n/m\] format]' \
    '(-o --output-directory --stdout)'{-o,--output-directory}'[store resulting files in given directory]:directory:_directories' \
    '(-o --output-directory --mbox)--stdout[output the generated mbox on standard output (implies --mbox)]' \
    '(-s --signoff)'{-s,--signoff}'[add "Signed-off-by:" line to the commit message]' \
    '--attach[create attachments instead of inlining patches]' \
    ':their revision:__git_revisions' \
    '::my revision:__git_revisions' && ret=0
}

# TODO: something better
_git-grep () {
  service=grep _grep
  ret=0
}

# TODO: this isn't strictly right, but close enough
_git-log () {
  _git-rev-list
}

# TODO: repository needs fixing
_git-ls-remote () {
  _arguments \
    '(-h --heads)'{-h,--heads}'[show only refs under refs/heads]' \
    '(-t --tags)'{-t,--tags}'[show only refs under refs/tags]' \
    ':repository:__git_any_repositories' \
    '*: :__git_references' && ret=0
}

_git-merge () {
  _arguments \
    $merge_args \
    $merge_strategy \
    ':merge message' \
    ':head:__git_revisions' \
    '*:remote:__git_revisions' && ret=0
}

_git-mv () {
  _arguments \
    '-f[force renaming/moving even if targets exist]' \
    '-k[skip move/renames that would lead to errors]' \
    '-n[only show what would happen]' \
    '*:source:__git_cached_files' \
    ':destination:_files' && ret=0
}

_git-octupus () {
  _nothing
}

_git-pull () {
  _arguments \
    $merge_args \
    $merge_strategy \
    $common_fetch_args \
    ':repository:__git_any_repositories' \
    '*:refspec:__git_ref_specs' && ret=0
}

_git-push () {
  _arguments \
    $force_ref_arg \
    '--all[fetch all refs]' \
    '--tags[all tags under "$GIT_DIR/refs/tags" are pushed]' \
    ':repository:__git_any_repositories' \
    '*:refspec:__git_ref_specs' && ret=0
}

_git-rebase () {
  if [[ $words[2] == (--abort|--continue) ]]; then
    _message 'no more options'
  else
    _arguments \
      '--onto[start new branch with HEAD equal to "newbase"]:newbase' \
      '--continue[continue after failure]' \
      '--abort[abort current rebase]' \
      ':upstream branch:__git_revisions' \
      '::working branch:__git_revisions' && ret=0
  fi
}

_git-repack () {
  _arguments \
    '-a[pack all objects into a single pack]' \
    '-d[remove redundant packs after packing]' \
    '-f[pass "--no-reuse-delta" option to "git pack-objects"]' \
    '-l[pass "--local" option to "git pack-objects"]' \
    '-n[do not update server information]' \
    '-q[pass "-q" option to "git pack-objects"]' && ret=0
}

_git-rerere () {
  _nothing
}

_git-reset () {
  _arguments \
    '(        --soft --hard)--mixed[like --soft but report what has not been updated (default)]' \
    '(--mixed        --hard)--soft[do not touch the index file nor the working tree]' \
    '(--mixed --soft       )--hard[match the working tree and index to the given tree]' \
    '::commit-ish:__git_revisions' && ret=0
}

_git-resolve () {
  _arguments \
    ':current commit:__git_revisions' \
    ':merged commit:__git_revisions' \
    ':commit message' && ret=0
}

_git-revert () {
  _arguments \
    '(-e --edit --no-edit)'{-e,--edit}'[edit the commit before committing the revert]' \
    '(-e --edit --no-edit)--no-edit[do not start the commit message editor]' \
    '(-n --no-commit)'{-n,--no-commit}'[do not commit the reversion]' \
    ':commit:__git_revisions' && ret=0
}

_git-shortlog () {
  _nothing
}

_git-show-branch () {
  _arguments \
    '--all[show all refs under $GIT_DIR/refs]' \
    '--current[include current branch to the list of revs]' \
    '--heads[show all refs under $GIT_DIR/refs/heads]' \
    '--tags[show all refs under $GIT_DIR/refs/tags]' \
    '--topo-order[show commits in topological order]' \
    '(--more --list --independent --merge-base)--independent[show only the reference that can not be reached from any of the other]' \
    '(--more --list --independent --merge-base)--list[synonym to "--more=-1"]' \
    '(--more --list --independent --merge-base)--merge-base[act like "git-merge-base -a" but with two heads]' \
    '(--more --list --independent --merge-base)--more=-[go given number of commit beyond common ancestor (no ancestry if negative)]:number' \
    '(--no-name --sha1-name)--no-name[do not show naming strings for each commit]' \
    '(--no-name --sha1-name)--sha1-name[name commits with unique prefix of object names]' \
    '*:reference:__git_revisions' && ret=0
}

_git-status () {
  _nothing
}

_git-verify-tag () {
  _arguments \
    ':tag:__git_tag_ids' && ret=0
}

# TODO: this should be a combination of git-rev-list and git-diff-tree
_git-whatchanged () {
  _arguments \
    '-p[show textual diffs]' \
    '--max-count=-[imit output to N commits]: :_guard "[[\:digit\:]]#" number' \
    '-r[how git internal diff output for the whole tree]' \
    $pretty_arg \
    '-m[show differences from all parents]' \
    '*:commit id:__git_commits2' && ret=0
}

_git-applypatch () {
  _arguments \
    ':message file:_files' \
    ':patch file:_files' \
    ':info file:_files' \
    '::signoff file:__git_signoff_file' && ret=0
}

# TODO: archive/branch can use _arch_archives perhaps?
_git-archimport () {
  _arguments \
    '-a[auto-register archives at http://mirrors.sourcecontrol.net]' \
    '-D[attempt to import trees that have been merged from]: :_guard "[[\:digit\:]]#" depth' \
    '-f[use the fast patchset import strategy]' \
    $help_arg \
    '-o[use old-style branch names]' \
    '-T[create a tag for every commit]' \
    '-t[use given directory as temporary directory]:directory:_directories' \
    $verbose_arg \
    ':archive/branch' \
    '::archive/branch'
}

_git-convert-objects () {
  _nothing
}

_git-cvsexportcommit () {
  _arguments \
    '-c[commit automatically if the patch applied cleanly]' \
    '-p[be pedantic (paranoid) when applying patches]' \
    $verbose_arg \
    $help_arg \
    '::parent commit id:__git_commits' \
    ':commit id:__git_commits' && ret=0
}

# TODO: _cvs_root for -d would be nice
_git-cvsimport () {
  _arguments \
    $author_conversion_file_arg \
    '-C[specify the git repository to import into]:directory:_directories' \
    '-d[specify the root of the CVS archive]:cvsroot' \
    $help_arg \
    '-i[do not perform a checkout after importing]' \
    '-k[remove keywords from source files in the CVS archive]' \
    '-m[attempt to detect merges based on the commit message]' \
    '-M[attempt to detect merges based on the commit message with custom pattern]:pattern' \
    '-o[specify the branch into which you wish to import]:branch' \
    '-P[read cvsps output file]:file:_files' \
    '-p[specify additionaly options for cvsps]:cvsps-options' \
    '-s[substitute the "/" character in branch names with given substitution]:substitute' \
    '-u[convert underscores in tag and branch names to dots]' \
    $verbose_arg \
    '-z[specify timestamp fuzz factor to cvsps]:fuzz-factor' \
    ':cvsmodule' && ret=0
}

_git-cvsexportcommit () {
  _arguments \
    '-c[commit automatically if the patch applies cleanly]' \
    '-p[be pedantic/paranoid when applying patches]' \
    $verbose_arg && ret=0
}

_git-lost-found () {
  _nothing
}

# TODO: something better
_git-merge-one-file () {
  _message "you probably should not be issuing this command"
}

_git-prune () {
  _arguments -S \
    '-n[do not remove anything; just report what would have been removed]'
    '*::heads:__git_heads' && ret=0
}

_git-relink () {
  _arguments \
    '--safe[stop if two objects with the same hash exist but have different sizes]' \
    ':directory:_directories' \
    '*:directory:_directories' && ret=0
}

# TODO: import stuff from _svn
_git-svnimport () {
  _arguments \
    $author_conversion_file_arg \
    '-b[specify the name of the SVN branches directory]:directory:_directories' \
    '-C[specify the git repository to import into]:directory:_directories' \
    '-d[use direct HTTP-requests if possible for logs only]:path' \
    '-D[use direct HTTP-requests if possible]:path' \
    $help_arg \
    '-i[do not perform a checkout after importing]' \
    '-l[limit the number of SVN changesets to pull]: :_guard "[[\:digit\:]]#" number' \
    '-m[attempt to detect merges based on the commit message]' \
    '-M[attempt to detect merges based on the commit message with custom pattern]:pattern' \
    '-o[specify the branch into which you wish to import]:branch' \
    '-r[prepend "rX: " to commit messages, where X is the subversion revision]' \
    '-s[specify the change number to start importing from:start-revision' \
    '-T[specify the name of the SVN tags directory]:directory:_directories' \
    '-t[specify the name of the SVN trunk]:trunk:_directories' \
    $verbose_arg \
    ':svn-repositry-url:_urls' \
    '::directory:_directories' && ret=0
}

# TODO: how do we complete argument 1?
# TODO: argument 2 should be __git_heads, but with full path
_git-symbolic-ref () {
  _arguments \
    ':symbolic reference' \
    '::reference' && ret=0
}

# TODO: first argument right?
# TODO: document options once they are in man
# key-id for -u could perhaps be completed from _gpg somehow
_git-tag () {
  local message=

  if (( $words[(I)-[asu]] )); then
    message='-m[specify tag message]'
  fi

  _arguments \
    $message \
    ':tag-name:__git_tags' \
    '::head:__git_revisions' \
    - '(creation)' \
      '(-s -u)-a[annotate]' \
      '-f[create a new tag even if one with the same name already exists]' \
      '(-a -u)-s[annotate and sign]' \
      '(-a -s)-u[annotate and sign with given key-id]:key-id' \
    - '(deletion)' \
      '-d[delete]:tag:__git_tags' && ret=0
}

_git-update-ref () {
  _arguments \
    ':symbolic reference:__git_revisions' \
    ':new reference:__git_revisions' \
    '::old reference:__git_revisions' && ret=0
}

_git-check-ref-format () {
  _arguments \
    ':reference:__git_revisions' && ret=0
}


_git-cherry () {
  _arguments \
    $verbose_arg \
    ':upstream:__git_revisions' \
    '::head:__git_revisions' && ret=0
}

_git-count-objects () {
  _arguments \
    '-v[also report number of in-pack objects and objects that can be removed]' && ret=0
}

# TODO: do better than _directories?  The directory needs to be a git-repository,
# so one could check for a required file in the given directory.
_git-daemon () {
  _arguments -S \
    '--strict-paths[match paths exactly]' \
    '--base-path=[remap all the path requests as relative to the given path]:path:_directories' \
    '--export-all[allow pulling from all repositories without verification]' \
    '(--port)--inetd[run server as an inetd service]' \
    '(--inetd)--port=-[specify port to listen to]' \
    '--init-timeout=-[specify timeout between connection and request]' \
    '--timeout=-[specify timeout for sub-requests]' \
    '--syslog[log to syslog instead of stderr]' \
    '--user-path=[allow ~user notation to be used in requests]::path:_directories' \
    '--verbose[log details about incoming connections and requested files]' \
    '*:repository:_directories' && ret=0
}

_git-get-tar-commit-id () {
  _message 'no arguments allowed; accepts tar-file on standard input'
}

_git-mailinfo () {
  _arguments \
    '-k[do not strip/add \[PATCH\] from the first line of the commit message]' \
    '(-u --encoding)-u[encode commit information in UTF-8]' \
    '(-u --encoding)--encoding[encode commit information in given encoding]:encoding' \
    ':message file:_files' \
    ':patch file:_files' && ret=0
}

# TODO:
# /* Backwards compatibility: if no -o specified, accept
#    <mbox> <dir> or just <dir> */
_git-mailsplit () {
  _arguments -S \
    '-b[if file does not begin with "From " line, assume it is a single mail message]' \
    '-d-[specify number of leading zeros]: :_guard "[[\:digit\:]]#" precision' \
    '-f-[skip the first N numbers]: :_guard "[[\:digit\:]]#" number' \
    '-o-[directory in which to place individual messages]:directory:_directories' \
    '*::mbox file:_files' && ret=0
}

_git-patch-id () {
  _message 'no arguments allowed; accepts patch on standard input'
}

_git-request-pull () {
  _arguments \
    ':start commit:__git_revisions' \
    ':url:_urls' \
    '::end commit:__git_revisions'
}

_git-rev-parse () {
  _arguments \
    '(--revs-only --no-revs)--revs-only[do not output flags and parameters not meant for "git-rev-list"]' \
    '(--revs-only --no-revs)--no-revs[do not output flags and parameters meant for "git-rev-list"]' \
    '(--flags --no-flags)--flags[do not output non-flag parameters]' \
    '(--flags --no-flags)--no-flags[do not output flag parameters]' \
    '--default[use "arg" if there is no parameter given]:arg' \
    '--verify[verify parameter to be usable]' \
    '--sq[output single shell-quoted line]' \
    '--not' \
    '--symbolic' \
    '--all[show all refs found in "$GIT_DIR/refs"]' \
    '--show-prefix[show path of current directory relative to top-leve directory]' \
    '--show-cdup[show path of top-level directory relative to current directory]' \
    '--git-dir[show "$GIT_DIR" if defined else show path to ".git" directory]' \
    '--short=-[show only handful hexdigits prefix]: :_guard "[[\:digit\:]]#" number' \
    {--after=-,--since=-}'[show "--max-age=" parameter corresponding given date string]:datestring' \
    {--berore=-,--until=-}'[show "--min-age=" parameter corresponding given date string]:datestring' \
    '*:args' && ret=0
}

_git-rm () {
  _arguments -S \
    '-f[remove files from the working tree as well as from the index]' \
    '-n[do actually remove the files, just show if they exist in the index]' \
    $verbose_arg \
    '*:files:__git_cached_files' && ret=0
}

_git-send-email () {
  _arguments \
    '--cc=[starting "Cc:" value for each email]:email' \
    '(--no-chain-reply-to)--chain-reply-to[each email will be sent as a reply to the previous one sent]' \
    '(--chain-reply-to)--no-chain-reply-to[all emails after the first will be sent as replies to the first one]' \
    '--compose[use $EDITOR to edit an introductory message for the patch series]' \
    '--from=[specify the sender of the emails]:email' \
    '--in-reply-to=[specify the contents of the first In-Reply-To header]:message-id' \
    '--no-signed-off-by-cc[do not add emails foudn in "Signed-off-by:" lines to the "Cc:" list]' \
    '--quiet[be less verbose]' \
    '--smtp-server=[specify the outgoing smtp server]:smtp server:_hosts' \
    '--subject=[specify the initial subject of the email thread]:subject' \
    '--suppress-from[do not add the "From:" address to the "Cc:" list]' \
    '--to=[specify the primary recipient of the emails]:email' \
    '*:file:_files' && ret=0
}

_git-stripspace () {
  _message 'no arguments allowed; accepts input file on standard input'
}

# ---

__git_guard () {
  typeset -A opts

  zparseopts -K -D -A opts M: J: V: 1 2 n F: X:

  [[ "$PREFIX$SUFFIX" != $~1 ]] && return 1

  if (( $+opts[-X] )); then
    _message -r $opts[-X]
  else
    _message -e $2
  fi

  [[ -n "$PREFIX$SUFFIX" ]]
}

__git_objects () {
  __git_guard $* "[[:xdigit:]]#" "object"
}

__git_trees () {
  __git_guard $* "[[:xdigit:]]#" "tree"
}

__git_tree_ishs () {
  __git_guard $* "[[:xdigit:]]#" "tree-ish"
}

__git_blobs () {
  _git_guard $* "[[:xdigit:]]#" 'blob id'
}

__git_stages () {
  __git_guard $* "[[:digit:]]#" 'stage'
}

__git_files () {
  local expl files ls_opts opts

  zparseopts -D -E -a opts -- -cached -deleted -modified -others -ignored -unmerged -killed

  ls_opts=( "--exclude-per-directory=.gitignore" )
  [[ -f "$gitdir/info/exclude" ]] && ls_opts=( $ls_opts "--exclude-from=$gitdir/info/exclude")

  files=("${(@f)$(GIT_DIR="$gitdir" git-ls-files "$ls_opts[@]" "$opts[@]" 2>/dev/null)}")
  if (( $? == 0 )); then
    _wanted files expl 'index file' _multi_parts $@ - / files
  else
    _message 'not a git repository'
  fi
}

__git_cached_files () {
  __git_files $* --cached
}

__git_deleted_files () {
  __git_files $* --deleted
}

__git_ignored_files () {
  __git_files $* --ignored
}

__git_killed_files () {
  __git_files $* --killed
}

__git_modified_files () {
  __git_files $* --modified
}

__git_other_files () {
  __git_files $* --others
}

__git_unmerged_files () {
  __git_files $* --unmerged
}

__git_tree_files () {
  local expl tree_files

  tree_files=(${"${(@f)$(git-ls-tree -r $@[-1] 2>/dev/null)}"#*$'\t'})
  if (( $? == 0 )); then
    _wanted files expl 'tree file' _multi_parts $@[1,-2] - / tree_files
  else
    _message 'not a git repository'
  fi
}

# TODO: deal with things that __git_heads and __git_tags has in common (i.e.,
# if both exists, they need to be completed to heads/x and tags/x.
__git_commits () {
  _alternative \
    'heads::__git_heads' \
    'tags::__git_tags'
}

# TODO: deal with prefixes and suffixes listed in git-rev-parse
__git_revisions () {
  __git_commits $*
}

__git_commits2 () {
  compset -P '\\\^'
  __git_commits
}

# FIXME: these should be imported from _ssh
# TODO: this should take -/ to only get directories
_remote_files () {
  # There should be coloring based on all the different ls -F classifiers.
  local expl rempat remfiles remdispf remdispd args suf ret=1

  if zstyle -T ":completion:${curcontext}:files" remote-access; then
    zparseopts -D -E -a args p: 1 2 4 6 F:
    if [[ -z $QIPREFIX ]]
    then rempat="${PREFIX%%[^./][^/]#}\*"
    else rempat="${(q)PREFIX%%[^./][^/]#}\*"
    fi
    remfiles=(${(M)${(f)"$(_call_program files ssh $args -a -x ${IPREFIX%:} ls -d1FL "$rempat" 2>/dev/null)"}%%[^/]#(|/)})
    compset -P '*/'
    compset -S '/*' || suf='remote file'

#    remdispf=(${remfiles:#*/})
    remdispd=(${(M)remfiles:#*/})

    _tags files
    while _tags; do
      while _next_label files expl ${suf:-remote directory}; do
#        [[ -n $suf ]] && compadd "$@" "$expl[@]" -d remdispf \
#	    ${(q)remdispf%[*=@|]} && ret=0 
	compadd ${suf:+-S/} "$@" "$expl[@]" -d remdispd \
	    ${(q)remdispd%/} && ret=0
      done
      (( ret )) || return 0
    done
    return ret
  else
    _message -e remote-files 'remote file'
  fi
}

__git_remote_repository () {
  local service

  service= _ssh

  if compset -P '*:'; then
    _remote_files
  else
    _alternative \
      'directories::_directories' \
      'hosts:host:_ssh_hosts -S:'
  fi
}

# should also be $GIT_DIR/remotes/origin
__git_any_repositories () {
  _alternative \
    'directories::_directories' \
    'remote repositories::__git_remote_repository'
}

__git_ref_specs () {
  if compset -P '*:'; then
    __git_heads
  else
    compset -P '+'
    if compset -S ':*'; then
      __git_heads
    else
      __git_heads -S ':'
    fi
  fi
}

__git_signoff_file () {
  _alternative \
    'signoffs:signoff:(yes true me please)' \
    'files:signoff file:_files'
}

__git_tag_ids () {
}

__git_heads_or_tags () {
  local expl
  typeset -a refs opts
  typeset -A ours

  zparseopts -K -D -a opts S: M: J: V: 1 2 n F: X: P:=ours T:=ours

  (( $+ours[-P] )) || ours[-P]=./.

  refs=(${${"${(@f)$(git ls-remote --$ours[-T] $ours[-P] 2>/dev/null)}"#*$'\t'}#refs/$ours[-T]/})
  if (( $? == 0 )); then
    _wanted $ours[-T] expl $ours[-T] compadd $opts - $refs
  else
    _message 'not a git repository'
  fi
}

__git_heads () {
  __git_heads_or_tags $* -T heads && ret=0
}

__git_tags () {
  __git_heads_or_tags $* -T tags && ret=0
}

# TODO: depending on what options are on the command-line already, complete
# only tags or heads
# TODO: perhaps caching is unnecessary.  usually won’t contain that much data
# TODO: perhaps provide alternative here for both heads and tags (and use
# __git_heads and __git_tags)
# TODO: instead of "./.", we should be looking in the repository specified as
# an argument to the command (but default to "./." I suppose (why not "."?))
__git_references () {
#  _alternative \
#    'heads::__git_heads' \
#    'tags::__git_tags' && ret=0
  local expl

  # TODO: deal with GIT_DIR
  if [[ $_git_refs_cache_pwd != $PWD ]]; then
    _git_refs_cache=(${${"${(@f)$(git ls-remote ./. 2>/dev/null)}"#*$'\t'}#refs/(heads|tags)/})
    _git_refs_cache_pwd=$PWD
  fi

  if (( $? == 0 )); then
    _wanted references expl 'references' compadd - $_git_refs_cache
  else
    _message 'not a git repository'
  fi
}

__git_local_references () {
  local expl

  if [[ $_git_local_refs_cache_pwd != $PWD ]]; then
    _git_local_refs_cache=(${${"${(@f)$(git ls-remote ./. 2>/dev/null)}"#*$'\t'}#refs/})
    _git_local_refs_cache_pwd=$PWD
  fi

  if (( $? == 0 )); then
    _wanted references expl 'references' compadd - $_git_local_refs_cache
  else
    _message 'not a git repository'
  fi
}

__git_repo-config_name () {
  __git_guard $* "[[:print:]]#" "name"
}

# ---

__git_is_indexed () {
  [[ -n $(git ls-files $REPLY) ]]
}

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: git checkout and git clone completion problems
  2006-05-24 16:10 ` Nikolai Weibull
@ 2006-05-24 16:25   ` Peter Stephenson
  2006-05-24 16:29   ` Hannu Koivisto
  1 sibling, 0 replies; 4+ messages in thread
From: Peter Stephenson @ 2006-05-24 16:25 UTC (permalink / raw)
  To: Zsh Users' List

It might be better to write the first line of the function definition as

#compdef -p git(|-*)

-- 
Peter Stephenson <pws@csr.com>                  Software Engineer
CSR PLC, Churchill House, Cambridge Business Park, Cowley Road
Cambridge, CB4 0WZ, UK                          Tel: +44 (0)1223 692070


To access the latest news from CSR copy this link into a web browser:  http://www.csr.com/email_sig.php


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: git checkout and git clone completion problems
  2006-05-24 16:10 ` Nikolai Weibull
  2006-05-24 16:25   ` Peter Stephenson
@ 2006-05-24 16:29   ` Hannu Koivisto
  1 sibling, 0 replies; 4+ messages in thread
From: Hannu Koivisto @ 2006-05-24 16:29 UTC (permalink / raw)
  To: Nikolai Weibull; +Cc: Zsh Users' List

"Nikolai Weibull" <now@bitwi.se> writes:

> Here's a link to it inside the git repository in which it is being
> maintained:

Thanks.  I took the attached version and it seems the clone problem
no longer exists.  The checkout problem still occurs, however. 

I also note that "git log" completion suffers from the \^\{\}
thing and it supports completion of neither ranges nor files.

-- 
Hannu


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2006-05-24 16:29 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2006-05-24 14:23 git checkout and git clone completion problems Hannu Koivisto
2006-05-24 16:10 ` Nikolai Weibull
2006-05-24 16:25   ` Peter Stephenson
2006-05-24 16:29   ` Hannu Koivisto

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