Можно ли использовать Git для поиска совпадающих имен файлов в репозитории?
Просто скажу, что у меня есть файл: "HelloWorld.pm" в нескольких подкаталогах в репозитории Git.
Я хотел бы выпустить команду для поиска полных путей для всех файлов, соответствующих "HelloWorld.pm":
Например:
/path/to/repository/HelloWorld.pm
/path/to/repository/but/much/deeper/down/HelloWorld.pm
/path/to/repository/please/dont/make/me/search/through/the/lot/HelloWorld.pm
Как я могу использовать Git для эффективного поиска всех полных путей, соответствующих указанному имени файла?
Я понимаю, что могу сделать это с помощью команды поиска Linux/Unix, но я надеялся избежать сканирования всех подкаталогов, ищущих экземпляры имени файла.
Ответы
Ответ 1
git ls-files
предоставит вам список всех файлов в репозитории. Вы можете передать шаблон, чтобы получить файлы, соответствующие этому шаблону.
git ls-files '*/HelloWorld.pm'
Если вы хотите найти набор файлов и grep через их содержимое, вы можете сделать это с помощью git grep
:
git grep some-string -- '*/HelloWorld.pm'
Ответ 2
Хм, исходный вопрос был о репозитории. Репозиторий содержит более 1 фиксации (в общем случае, по крайней мере), но ответы перед поиском только через одну фиксацию.
Поскольку я не мог найти ответ, который действительно ищет всю историю фиксации, я написал быструю грубую силу script git -find-by-name, которая учитывает (почти) все фиксации.
#! /bin/sh
tmpdir=$(mktemp -td git-find.XXXX)
trap "rm -r $tmpdir" EXIT INT TERM
allrevs=$(git rev-list --all)
# well, nearly all revs, we could still check the log if we have
# dangling commits and we could include the index to be perfect...
for rev in $allrevs
do
git ls-tree --full-tree -r $rev >$tmpdir/$rev
done
cd $tmpdir
grep $1 *
Возможно, есть более элегантный способ.
Обратите внимание на тривиальный способ передачи параметра в grep, чтобы он соответствовал частям имени файла. Если это нежелательно, привяжите ваше выражение поиска и/или добавьте соответствующие опции grep.
Для глубоких историй выход может быть слишком шумным, я думал о script, который преобразует
список изменений в диапазоне, как противоположность тому, что может сделать git rev-list. Но до сих пор это оставалось мыслью.
Ответ 3
Try:
git ls-tree -r HEAD | grep HelloWorld.pm
Ответ 4
git ls-files | grep -i HelloWorld.pm
grep -i делает регистр grep нечувствительным.
Ответ 5
[Немного о злоупотреблении комментариями, я признаю, но я не могу комментировать и думал, что улучшу ответ @uwe-geuder.]
#!/bin/bash
#
#
# I'm using a fixed string here, not a regular expression, but you can easily
# use a regular expression by altering the call to grep below.
name="$1"
# Verify usage.
if [[ -z "$name" ]]
then
echo "Usage: $(basename "$0") <file name>" 1>&2
exit 100
fi
# Search all revisions; get unique results.
while IFS= read rev
do
# Find $name in $rev tree and only use its path.
grep -F -- "$name" \
<(git ls-tree --full-tree -r "$rev" | awk '{ print $4 }')
done < \
<(git rev-list --all) \
| sort -u
Опять же, +1 для @uwe-geuder для отличного ответа.
Если вас интересует сама BASH:
Если вам не гарантировано разделение слов в цикле for (как при использовании такого массива: for item in "${array[@]}"
), я настоятельно рекомендую использовать while IFS= read var ; do ... ; done < <(command)
, когда вывод команды, который вы перебираете, разделяется newlines (или read -d''
, когда вывод разделяется пустой строкой $'\0'
). В то время как git rev-list --all
гарантированно использует 40-байтные шестнадцатеричные строки (без пробелов), я никогда не хочу рисковать. Теперь я могу легко изменить команду из git rev-list --all
на любую команду, которая создает строки
Я также рекомендую использовать встроенные механизмы BASH для ввода ввода и вывода фильтра вместо временных файлов.
Ответ 6
script от Uwe Geuder (@uwe-geuder) отлично, но нет необходимости выгружать каждый из выходов ls-tree в свой собственный каталог, нефильтрованный.
Гораздо быстрее и с меньшим объемом памяти: запустите grep на выходе, а затем сохраните его, как показано в этом gist