Git Bash-завершение с поддержкой имени файла? - PullRequest
10 голосов
/ 17 мая 2011

есть ли скрипт завершения bash, который поддерживает завершение имени файла?Я использую в основном Mercurial и там я могу набрать:

hg diff test/test_<tab>

, и он покажет / завершит все измененные тестовые файлы.Это работает для большинства подкоманд, то есть hg add <tab><tab> будет отображать только неотслеживаемые файлы.Это действительно удобно.

Скрипт bash из git contrib seams не поддерживает это.Есть ли альтернативы или как вы работаете с git в командной строке?

Edit 2015

git-completion.bash поддерживает полное завершение имени файла, начиная с ~ 1.80,2

Ответы [ 4 ]

13 голосов
/ 17 мая 2011

Итак, давайте посмотрим, как это делает скрипт завершения Mercurial bash.

Это важная часть :

_hg_status()
{
    local files="$(_hg_cmd status -n$1 .)"
    local IFS=$'\n'
    COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$files' -- "$cur"))
}

Здесь его называют :

_hg_command_specific()
{
    case "$cmd" in 
    [...]
    diff)
        _hg_status "mar"
    ;;
    [...]
    esac
    return 0
}

Таким образом, это просто вызов hg status -nmar и использование вывода в качестве списка файлов для завершения.

Я думаю, что было бы не слишком сложно вставить что-то похожее в скрипт завершения git - нам нужно изменить здесь __git_diff, чтобы не делать простое имя файла + завершение ветви, а вызывать git status вместо.


Команды

git status --porcelain | grep '^.[^ ?]' | cut -b 4-

(для git diff --cached) и

git status --porcelain | grep '^[^ ?]' | cut -b 4-

(для git diff), кажется, выводит правильную вещь (если переименований нет).

Они оба бесполезны, если сравнивать с чем-либо, кроме HEAD.

Более общий способ - использовать

git diff --relative --name-only [--cached] [commit1] [commit2]]

где commit1 и commit2 (и, возможно, --cached) происходят из уже заданной командной строки diff.


Я реализовал идею, изложенную выше, в bash и пропатчен в git-completion.bash. Если вы не хотите менять git-completion.bash, добавьте эти две функции в некоторый bash-файл и поставьте его после исходного git-completion.bash. Теперь он должен работать с такими командами, как

git diff -- <tab>
git diff --cached -- <tab>
git diff HEAD^^ -- <tab>
git diff origin/master master -- <tab>

Я отправил это как исправление в список рассылки git, давайте посмотрим, что из этого получится. (Я обновлю этот ответ, когда получу отзыв.)

# Completion for the file argument for git diff.
# It completes only files actually changed. This might be useful
# as completion for other commands as well.
#
# The idea comes from the bash completion for Mercurial (hg),
# which does something similar (but more simple, only difference of
# working directory to HEAD and/or index, if I understand right).
# It (the idea) was brought to us by the question
#      http://stackoverflow.com/q/6034472/600500
#  from "olt".
__git_complete_changed_files()
{
  #
  # We use "git diff --name-only --relative" to generate the list,
  # but this needs the same --cached and <commit> arguments as the
  # command line being constructed.
  #


    # first grab arguments like --cached and any commit arguments.

    local -a args=()
    local finish=false

    for (( i=1 ; i < cword ; i++)) do
    local current_arg=${words[$i]}
    #  echo checking $current_arg >&2
       case $current_arg in
           --cached)
               args+=( $current_arg )
               ;;
           --)
               # finish parsing arguments, the rest are file names
               break
               ;;
           -*)
               # other options are ignored
               ;;
           *)
               if git cat-file -e $current_arg 2> /dev/null
               then
                   case $( git cat-file -t $current_arg ) in
                       commit|tag)
                       # commits and tags are added to the command line.
                           args+=( $current_arg )
                           # echo adding $current_arg >&2
                           ;;
                       *)
                   esac
               fi
               ;;
       esac
    done

    # now we can call `git diff`

    COMPREPLY=( $( compgen \
        -W "$( git diff --name-only --relative "${args[@]}" -- )" -- $cur ) )
}

_git_diff ()
{
    if __git_has_doubledash
    then
        # complete for the file part: only changed files
        __git_complete_changed_files
    else
    case "$cur" in
    --*)
        __gitcomp "--cached --staged --pickaxe-all --pickaxe-regex
            --base --ours --theirs --no-index
            $__git_diff_common_options
            "
        return
        ;;
    esac
    __git_complete_revlist_file
    fi
}

Обновление: Похоже, этот патч не нужен в этой форме, так как текущий способ завершения файлов более полезен для людей, которые хотят проверить, есть ли изменения в каком-либо подкаталоге (например, завершить, когда вывод diff может быть пустым). Это может быть принято, если связано с некоторой переменной конфигурации (по умолчанию текущее поведение). Кроме того, отступ должен быть адаптирован к стандарту (см. Ответ от Junio ​​C Hamano ).

Я мог бы сделать еще один шаг, но не могу гарантировать это в ближайшем будущем. Если кто-то другой захочет это сделать, смело берите мой код, меняйте его и отправляйте снова.

3 голосов
/ 18 ноября 2013

Это решает проблему git diff <tab> для меня, поместите следующее в .bashrc :

alias gid='git diff'
__gdiff () {
    local cur prev opts
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"
    opts=$(git status --porcelain | grep '^.[^ ?]' | cut -b 4-)

    case "${prev}" in
        gid)
            COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
            ;;
    esac
}
complete -F __gdiff gid

и затем выполните gid <tab> вместо git diff <tab>.Это может быть упрощено, но, кажется, хорошо работает как быстрое решение.

0 голосов
/ 02 мая 2018

С 2011 года, как отмечает OP, Git поддерживает полное завершение имени файла, начиная с ~ 1.8.2.

Но с Git 2.18 (Q2 2018), завершение оболочки (в contrib/), которое дает списокпути были несколько оптимизированы.

См. коммит 78a2d21 (04 апреля 2018 г.) Клеменс Бучахер (drizzd) .
(объединено Junio ​​C Hamano - gitster - в коммит 3a940e9 , 25 апреля 2018)

completion: улучшение ls-filesпроизводительность фильтра

Из вывода ls-files мы удаляем все компоненты, кроме самого левого пути, а затем удаляем дубликаты.Мы делаем это в цикле while, который является узким местом в производительности, когда число итераций велико (например, для 60000 файлов в linux.git).

$ COMP_WORDS=(git status -- ar) COMP_CWORD=3; time _git

real    0m11.876s
user    0m4.685s
sys     0m6.808s

ЗаменаЦикл с командой cut значительно повышает производительность:

$ COMP_WORDS=(git status -- ar) COMP_CWORD=3; time _git

real    0m1.372s
user    0m0.263s
sys     0m0.167s

Измерения были выполнены с помощью Msys2 bash, который используется Git для Windows.

При фильтрации ls-files вывод мы позаботимся о том, чтобы не трогать абсолютные пути.Это избыточно, потому что ls-files никогда не будет выводить абсолютные пути.Удалите ненужные операции.

Первоначально сообщалось о проблеме Git для Windows, проблема 1533 .

0 голосов
/ 22 марта 2016

Не совсем ваш желаемый ответ, но я хотел, чтобы вы знали, что скоро fish (дружественная интерактивная оболочка) предоставит вам поддержку завершения файла git из коробки. Это в настоящее время в мастерской с выпуском 2.3.0 в ближайшее время.

https://github.com/fish-shell/fish-shell/issues/901
https://github.com/fish-shell/fish-shell/pull/2364
https://github.com/fish-shell/fish-shell/commit/c5c59d4acb00674bc37198468b5978f69484c628

Если у вас есть такой статус:

$ git status
modified: ../README.md
$ git add <tab>
:/README.md 

enter image description here

Вы также можете просто набрать README и нажать вкладку, и он вставит его для вас, если это единственный матч. Чертовски приятно!

...