11

I have a Git alias update that I would like to outfit with branch-name completion. The alias is defined like so:

[alias]
        update = "!f() { git push . origin/$1:$1; }; f"

(It updates a local tracking branch with its upstream version, without having to check out the branch. Not really important to the specific question, though.)

I would like the command to tab-complete existing branch names for its $1 argument. I know I can define a function called _git-update to control completion, but I'm missing some pieces to get it to work:

_git-update ()
{
  ***some-function-here*** "$(__git_branch_names)"
}

I am using the completions installed on OS X by brew install zsh-completions, which is the set at https://github.com/zsh-users/zsh-completions .

(This question is directly analogous to https://stackoverflow.com/a/41307951/169947, but for Zsh instead of Bash.)

Kache
  • 15,647
  • 12
  • 51
  • 79
Ken Williams
  • 22,756
  • 10
  • 85
  • 147
  • @Kache: what version of Git are you using? Branch completion recently improved with Git 2.31: https://stackoverflow.com/a/66192741/6309 – VonC Apr 15 '21 at 06:14
  • Latest, I have no git version constraints – Kache Apr 15 '21 at 15:42

4 Answers4

4

May be a bit preemptive, but this is working:

# provides completion options similar to git branch/rebase/log
_complete_like_git_branch() {
  __gitcomp_nl_append "FETCH_HEAD"
  __gitcomp_nl_append "HEAD"
  __gitcomp_nl_append "ORIG_HEAD"
  __gitcomp_nl_append "$(__git_heads)"
  __gitcomp_nl_append "$(__git_remote_heads)"
  __gitcomp_nl_append "$(__git_tags)"
  __gitcomp_nl_append "$(__git_complete_refs)"
}

_git_rebase_chain() { _complete_like_git_branch }

# my git "bang" alias of git log
_git_lgk() { _complete_like_git_branch }

reference: contrib/completion/git-completion.bash

Possible improvements:

  • Is the above canonically correct? i.e. Using global shell functions in ~/.zshrc?
  • Choices are super-similar to git rebase and git log, but are they the same?
Kache
  • 15,647
  • 12
  • 51
  • 79
2

If you want the same completions as another existing git subcommand (that the completion system knows about), easiest is like this:

[alias]
    update = "!f() { : git branch ; git push . origin/$1:$1; }; f"

The null command (:) followed by the git subcommand (e.g. git branch) tells the git completion system to complete your alias using the completions from the subcommand.

This functionality is built-in to the git alias system and using it means you don't have to worry about shell differences.

DylanYoung
  • 2,423
  • 27
  • 30
  • 1
    Thanks, this would be great if I can get it working - I now have that alias described exactly as above, but it doesn't seem to complete properly under git version 2.36.1. Could something else be getting in the way, or do I need to enable something else maybe? – Ken Williams Nov 24 '22 at 18:45
  • The only thing I can think off the top @Ken is that some systems package their own completions for git and perhaps your system is using non-native ones that don't support the null-comand-completion-spec for some reason. Unfortunately, haven't been able to figure out how to get the info on which completion is being used by zsh yet (their docs are crazy unhelpful and they have two different completion systems *sigh*). – DylanYoung Dec 30 '22 at 21:18
  • FWIW the argument for the alias above still gets completed with the file names for me with zsh 5.9 and Git 2.39.2 too. – VZ. Jul 02 '23 at 23:00
0

i use this function to append the working branch to my PS1:

    parse_git_branch() {
        git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/(\1)/'
    }

hbrandao
  • 3
  • 1
  • 4
0

It looks like the completions in Git sources are different from those actually used by zsh, at least for the zsh Debian package. This package installs /usr/share/zsh/functions/Completion/Unix/_git which explains how to customize the completions in a comment near the beginning and with it's just a matter of defining the following function:

_git-update() { __git_branch_names }

(which can/should be made autoloaded, of course).

FWIW I wanted to avoid completing the current branch name in my own alias and I couldn't see how to exclude it when using __git_branch_names, so I ended up doing this instead:

_git-my-alias () {
        head=$(git symbolic-ref HEAD)
        for b in $(ls -1 $(git rev-parse --git-dir)/refs/heads)
        do
                if [[ "refs/heads/$b" != $head ]]
                then
                        compadd $b
                fi
        done
}
VZ.
  • 21,740
  • 3
  • 39
  • 42