Как исключить определенные каталоги / файлы из поиска git grep

148

Есть ли способ исключить определенные пути / каталоги / файлы при поиске в репозитории git с помощью git grep? Что-то похожее на --excludeопцию в обычной grepкоманде?

Мне нужно использовать, git grepпотому что использование grepнапрямую работает слишком медленно в больших репозиториях git.

Йогешвер Шарма
источник
Возможным обходным
решением
8
Эта функция была добавлена ​​в 1.9.0, см. Мой ответ ниже
только

Ответы:

17

Это невозможно, но недавно это обсуждалось . Предлагаемый обходной путь по ссылке:

*.dllЗатем вы можете поместить в файл .gitignore git grep --exclude-standard.

ИЗМЕНИТЬ см. Только один ответ , так как git 1.9.0 это возможно.

CharlesB
источник
2
Раньше это было правдой, но теперь это возможно в git. Посмотрите, каким должен быть настоящий ответ ниже: stackoverflow.com/a/30084612/1391445
user1391445
211

В git 1.9.0 в s excludeбыло добавлено «волшебное слово» pathspec. Поэтому, если вы хотите искать foobarв каждом файле, кроме тех, которые соответствуют, *.javaвы можете сделать:

git grep foobar -- './*' ':(exclude)*.java'

Или используйте !"краткую форму" для исключения:

git grep foobar -- './*' ':!*.java'

Обратите внимание, что в версиях git до v2.12 при использовании исключения у pathspecвас должен быть хотя бы один «включительно» pathspec. В приведенных выше примерах это ./*(рекурсивно включить все в текущем каталоге). В git v2.13 это ограничение снято и git grep foobar -- ':!*.java'работает без расширения ./*.

Вы также можете использовать что-то вроде :(top)(короткая форма :), :/чтобы включить все, что находится в верхней части репозитория. Но тогда вы, вероятно, также захотите настроить исключение, pathspecчтобы оно начиналось сверху: :/!*.java(в противном случае он будет исключать только *.javaфайлы из вашего текущего каталога).

Там хорошая ссылка для всех «волшебных слов» разрешенных в pathspecв git-scm.com (или только git help glossary). По какой-то причине документы на kernel.org действительно устарели, хотя они часто появляются первыми при поиске в Google.

только никто
источник
4
git grep clock.gettime -- './*' ':!arch/**' ':!drivers/**'чтобы исключить несколько целых каталогов. Я не думаю, что это предотвращает рекурсию.
Чиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
2
Для частого использования, вы можете сделать GIT псевдоним с исключениями: git config alias.mygrep '!git grep "$@" -- "${GIT_PREFIX}/*" ":!*.java*" #'. Тогда просто git mygrep foobar. (Используя псевдоним shell # trick и текущий
каталог
проблема, которую я не могу решить с помощью этого решения, заключается в том, что указанные пути к файлам относятся к корню WC. Итак, если я нахожусь в подкаталоге WC, я не могу просто использовать путь к найденному файлу (-ам) как есть (например, для меньшего количества), но должен соединять общие пути. Есть ли решение для этого (без необходимости использовать sed)? [git bash на win7]
elonderin
2
@elonderin это решение не имеет ничего общего с тем, как сообщаются сопоставленные файлы. Но я просто попробовал подкаталоги git grepи git ls-filesиз подкаталогов, и имена файлов обоих отчетов относительно текущего каталога (даже если вы используете ':(top)'include pathspec). Обе команды имеют --full-nameвозможность сообщать имена относительно корня, но по умолчанию это отключено.
onlynone
1
Я не использую псевдонимы git, поэтому я сделал функцию bash, но, возможно, псевдоним git лучше gist.github.com/cmdcolin/04e2378b60f4457a41904c659368066f
Colin D
62

Обновление: для git> = 1.9 есть встроенная поддержка шаблонов исключения, см. Только один ответ .

Это может показаться обратным, но вы можете передать список файлов, не соответствующих вашему шаблону исключения, git grepследующим образом:

git grep <pattern> -- `git ls-files | grep -v <exclude-pattern>`

grep -vвозвращает все пути, которые не совпадают <exclude-pattern>. Обратите внимание, что git ls-filesтакже принимает --excludeпараметр, но он применяется только к неотслеживаемым файлам .

Кинан
источник
Спасибо за это! Git grep намного быстрее, чем ack & co, но невозможность исключить произвольные пути было слишком неудобно, так сказать :)
Томаш Зелински
2
К сожалению, в моем репо много файлов. Когда я пробую подход @kynan, я получаю: «-bash: / usr / bin / git: слишком длинный список аргументов»
Бениссимо,
2
Это должно решить как проблему «Слишком длинный список аргументов» Benissimo, так и мою проблему с символами файлов, интерпретируемыми bash (например, []), или именами файлов, содержащими пробелы в репозитории: git ls-files | grep -v <exclue-pattern> | xargs -d '\ n' git grep <pattern> -
Scout
2
Отметьте onlynone ответ, возможно, сделать это полностью в (современных версиях) git.
Дэвид
Почему отрицательные голоса? Этот ответ по-прежнему применим к версиям git до 1.9. Я добавил примечание, касающееся только одного ответа.
kynan
6

Вы можете пометить файлы или каталоги как двоичные, создав файл атрибутов в вашем репозитории, например

$ cat .git/info/attributes 
directory/to/ignore/*.* binary
directory/to/ignore/*/*.* binary
another_directory/to/also/ignore/*.* binary

Совпадения в двоичных файлах перечислены без строки включения, например

$ git grep "bar"
Binary file directory/to/ignore/filename matches
other_directory/other_filename:      foo << bar - bazz[:whatnot]
Коберлин
источник
2

Используя пример @kynan в качестве основы, я создал этот скрипт и поместил его в свой путь ( ~/bin/) как gg. Он использует, git grepно избегает некоторых указанных типов файлов.

В нашем репо много изображений, поэтому я исключил файлы изображений, и это сокращает время просмотра до 1/3, если я просматриваю все репо. Но сценарий можно легко изменить, чтобы исключить другие типы файлов или шаблоны geleral.

#!/bin/bash                                                                    
#                                                                              
# Wrapper of git-grep that excludes certain filetypes.                         
# NOTE: The filetypes to exclude is hardcoded for my specific needs.           
#                                                                              
# The basic setup of this script is from here:                                 
#   https://stackoverflow.com/a/14226610/42580                                  
# But there is issues with giving extra path information to the script         
# therefor I crafted the while-thing that moves path-parts to the other side   
# of the '--'.                                                                 

# Declare the filetypes to ignore here                                         
EXCLUDES="png xcf jpg jpeg pdf ps"                                             

# Rebuild the list of fileendings to a good regexp                             
EXCLUDES=`echo $EXCLUDES | sed -e 's/ /\\\|/g' -e 's/.*/\\\.\\\(\0\\\)/'`      

# Store the stuff that is moved from the arguments.                            
moved=                                                                         

# If git-grep returns this "fatal..." then move the last element of the        
# arg-list to the list of files to search.                                     
err="fatal: bad flag '--' used after filename"                                 
while [ "$err" = "fatal: bad flag '--' used after filename" ]; do              
    {                                                                          
        err=$(git grep "$@" -- `git ls-files $moved | grep -iv "$EXCLUDES"` \  
            2>&1 1>&3-)                                                        
    } 3>&1                                                                     

    # The rest of the code in this loop is here to move the last argument in   
    # the arglist to a separate list $moved. I had issues with whitespace in   
    # the search-string, so this is loosely based on:                          
    #   http://www.linuxjournal.com/content/bash-preserving-whitespace-using-set-and-eval
    x=1                                                                        
    items=                                                                     
    for i in "$@"; do                                                          
        if [ $x -lt $# ]; then                                                 
            items="$items \"$i\""                                              
        else                                                                   
            moved="$i $moved"                                                  
        fi                                                                     
        x=$(($x+1))                                                            
    done                                                                       
    eval set -- $items                                                         
done                                                                           
# Show the error if there was any                                              
echo $err                                                                      

Примечание 1

В соответствии с этим должна быть возможность дать объекту имя git-ggи уметь вызывать его как обычную команду git, например:

$ git gg searchstring

Но я не могу заставить это работать. Я создал сценарий в своем ~/bin/и сделал git-ggсимволическую ссылку в /usr/lib/git-core/.

Заметка 2

Команду нельзя превратить в обычный shпсевдоним git, поскольку в этом случае она будет вызываться из корня репозитория. И я не этого хочу!

UlfR
источник