Как выполнить поиск (поиск) зафиксированного кода в истории Git? - PullRequest
1276 голосов
/ 28 мая 2010

Я удалил файл или некоторый код в файле когда-то в прошлом. Могу ли я получить доступ к содержимому (не к сообщениям о коммитах)?

Очень плохим решением является поиск в журнале:

git log -p | grep <pattern>

Однако, это не возвращает хеш коммита сразу. Я играл с git grep безрезультатно.

Ответы [ 14 ]

1696 голосов
/ 28 мая 2010

Для поиска коммита содержимого (т. Е. Фактических строк исходного текста, в отличие от сообщений коммита и т. П.), Вам нужно сделать:

git grep <regexp> $(git rev-list --all)

git rev-list --all | xargs git grep <expression> будет работать, если вы столкнетесь с ошибкой «Список аргументов слишком длинный».

Если вы хотите ограничить поиск каким-либо поддеревом (например, "lib / util"), вам нужно будет передать его подкоманде rev-list и grep:

git grep <regexp> $(git rev-list --all -- lib/util) -- lib/util

Это пролистает весь текст вашего коммита для regexp.

Причина прохождения пути в обеих командах заключается в том, что rev-list вернет список ревизий, где произошли все изменения в lib/util, но вам также нужно перейти к grep, чтобы он выполнял поиск только в lib/util.

Представьте себе следующий сценарий: grep может найти то же <regexp> в других файлах, содержащихся в той же ревизии, возвращенной rev-list (даже если в этой ревизии не было изменений в этом файле).

Вот несколько других полезных способов поиска вашего источника:

Поиск в рабочем дереве текста, соответствующего регулярному выражению regexp:

git grep <regexp>

Поиск в рабочем дереве строк текста, соответствующих регулярному выражению regexp1 или regexp2:

git grep -e <regexp1> [--or] -e <regexp2>

Поиск в рабочем дереве строк текста, соответствующих регулярным выражениям regexp1 и regexp2, только пути к файлам отчетов:

git grep -e <regexp1> --and -e <regexp2>

Поиск в рабочем дереве файлов, в которых строки текста соответствуют регулярному выражению regexp1, а строки текста соответствуют регулярному выражению regexp2:

git grep -l --all-match -e <regexp1> -e <regexp2>

Поиск в рабочем дереве для измененных строк шаблона соответствия текста:

git diff --unified=0 | grep <pattern>

Поиск во всех ревизиях текста, соответствующего регулярному выражению регулярное выражение:

git grep <regexp> $(git rev-list --all)

Поиск во всех ревизиях между rev1 и rev2 текста, соответствующего регулярному выражению regexp:

git grep <regexp> $(git rev-list <rev1>..<rev2>)
484 голосов
/ 28 мая 2010

Вы должны использовать опцию (-S) , равную git log

Для поиска Foo:

git log -SFoo -- path_containing_change 
git log -SFoo --since=2009.1.1 --until=2010.1.1 -- path_containing_change

См. История Git - найдите потерянную строку по ключевому слову , чтобы узнать больше.


Как Якуб Наребски прокомментировал:

  • этот ищет различия, которые вводят или удаляют экземпляр <string>.
    Обычно это означает "ревизии, в которых вы добавили или удалили строку с 'Foo'".

  • опция --pickaxe-regex позволяет использовать расширенное регулярное выражение POSIX вместо поиска строки.


Как заметил Роб , этот поиск чувствителен к регистру - он открыл дополнительный вопрос о том, как искать без учета регистра.

227 голосов
/ 14 сентября 2012

Мой любимый способ сделать это - опция git log -G (добавлена ​​в версии 1.7.4).

-G<regex>
       Look for differences whose added or removed line matches the given <regex>.

Существует небольшая разница между тем, как опции -G и -S определяют соответствие коммита:

  • Опция -S, по сути, подсчитывает, сколько раз ваш поиск соответствует файлу до и после коммита. Фиксация отображается в журнале, если значения до и после отличаются. Например, это не будет показывать коммиты, куда была перемещена строка, соответствующая вашему запросу.
  • С параметром -G фиксация отображается в журнале, если ваш поиск соответствует какой-либо строке, которая была добавлена, удалена или изменена.

Взять этот коммит в качестве примера:

diff --git a/test b/test
index dddc242..60a8ba6 100644
--- a/test
+++ b/test
@@ -1 +1 @@
-hello hello
+hello goodbye hello

Поскольку число раз, когда «hello» появляется в файле, одинаково до и после этой фиксации, оно не будет совпадать при использовании -Shello. Однако, поскольку в строке, соответствующей hello, было изменение, фиксация будет показана с использованием -Ghello.

44 голосов
/ 17 апреля 2014

Если вы хотите просмотреть изменения кода (посмотреть, что на самом деле было изменено с данным словом во всей истории), перейдите в режим patch - я нашел очень полезную комбинацию действий:

git log -p
# hit '/' for search mode
# type in the word you are searching
# if the first search is not relevant hit 'n' for next (like in vim ;) )
24 голосов
/ 17 ноября 2011

Я взял @ ответ Джита и перенес его в Windows (благодаря этому ответу ):

FOR /F %x IN ('"git rev-list --all"') DO @git grep <regex> %x > out.txt

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

19 голосов
/ 23 июня 2017

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

git log -p --all -S 'search string'
git log -p --all -G 'match regular expression'

В этом списке команд журнала фиксируются коммиты, которые добавляют или удаляют заданную строку поиска / регулярное выражение, (как правило), более новые в первую очередь. Опция -p позволяет отображать соответствующий diff, где шаблон был добавлен или удален, так что вы можете увидеть его в контексте.

Найдя соответствующий коммит, который добавляет искомый текст (например, 8beeff00d), найдите ветки, содержащие коммит:

git branch -a --contains 8beeff00d
17 голосов
/ 02 апреля 2015

Поиск в любая ревизия, любые файлы :

git rev-list --all | xargs git grep <regexp>

Поиск только в некоторых заданных файлах, например , пример XML-файлы:

git rev-list --all | xargs -I{} git grep <regexp> {} -- "*.xml"

Строки результата должны выглядеть так: 6988bec26b1503d45eb0b2e8a4364afb87dde7af: bla.xml: текст найденной строки ...

Затем вы можете получить дополнительную информацию, такую ​​как автор, дата, разница, используя git show:

git show 6988bec26b1503d45eb0b2e8a4364afb87dde7af
6 голосов
/ 01 октября 2015

Для всех, кто пытается сделать это в Sourcetree , в пользовательском интерфейсе нет прямой команды для него (начиная с версии 1.6.21.0). Однако вы можете использовать команды, указанные в принятом ответе, открыв окно Терминал (кнопка доступна на главной панели инструментов) и скопировав / вставив их в него.

Примечание: Поиск в Sourcetree * Поиск 1008 * может частично выполнять поиск текста для вас. Нажмите Ctrl + 3 , чтобы перейти к представлению «Поиск» (или нажмите вкладку «Поиск» внизу). В крайнем правом углу установите тип поиска на File Changes , а затем введите строку, которую хотите найти. Этот метод имеет следующие ограничения по сравнению с приведенной выше командой:

  1. Sourcetree показывает только коммитов , которые содержат поисковое слово в одном из измененных файлов. Поиск точного файла, содержащего текст поиска, снова является ручной задачей.
  2. RegEx не поддерживается.
4 голосов
/ 07 февраля 2018

Для простоты я бы предложил использовать графический интерфейс: gitk - Браузер репозитория Git . Это довольно гибкий

  1. Для поиска кода:

    Enter image description here
  2. Для поиска файлов:

    Enter image description here
  3. Конечно, он также поддерживает регулярные выражения:

    Enter image description here

И вы можете перемещаться по результатам, используя стрелки вверх / вниз.

2 голосов
/ 16 декабря 2014

@ Ответ Джита работает в PowerShell.

git grep -n <regex> $(git rev-list --all)

Ниже показаны все файлы в любом коммите, содержащие password.

# store intermediate result
$result = git grep -n "password" $(git rev-list --all)

# display unique file names
$result | select -unique { $_ -replace "(^.*?:)|(:.*)", "" }
...