Как выполнить поиск (поиск) зафиксированного кода в истории git?
Я удалил файл или некоторый код в файле когда-то в прошлом. Могу ли я получить доступ к содержимому (не к сообщениям о коммитах)?
Очень плохим решением является поиск в журнале:
git log -p | grep <pattern>
Однако это не сразу возвращает хеш коммита. Я играл с git grep
но безрезультатно.
22 ответа
Для поиска содержимого фиксации (т. Е. Фактических строк исходного текста, в отличие от сообщений фиксации и т. П.) Необходимо сделать следующее:
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
Это пролистает весь ваш текст коммита для регулярного выражения.
Причина прохождения пути в обеих командах заключается в том, что rev-list
вернет список ревизий, где все изменения lib/util
случилось, но также вам нужно перейти к grep
так что он будет искать только на lib/util
,
Просто представьте следующий сценарий: grep
может найти то же самое <regexp>
на других файлах, которые содержатся в той же ревизии, возвращенной rev-list
(даже если в этой ревизии не было изменений в этом файле).
Вот несколько других полезных способов поиска вашего источника:
Найдите в рабочем дереве текст, соответствующий регулярному выражению регулярное выражение:
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>
Поиск всех ревизий для текста, соответствующего регулярному выражению regexp:
git grep <regexp> $(git rev-list --all)
Поиск всех ревизий между rev1 и rev2 для текста, соответствующего регулярному выражению regexp:
git grep <regexp> $(git rev-list <rev1>..<rev2>)
Вы должны использовать кирку (-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 - найдите потерянную строку по ключевому слову для получения дополнительной информации.
Как прокомментировал Jakub Narębski:
это ищет различия, которые вводят или удаляют экземпляр
<string>
,
Обычно это означает "ревизии, в которых вы добавили или удалили строку с 'Foo'".--pickaxe-regex
опция позволяет вам использовать расширенное регулярное выражение POSIX вместо поиска строки.
Как прокомментировал Rob, этот поиск чувствителен к регистру - он открыл дополнительный вопрос о том, как искать без учета регистра.
Мой любимый способ сделать это с git log
"s -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
,
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
Если вы хотите просмотреть изменения кода (посмотреть, что на самом деле было изменено с данным словом во всей истории), перейдите к 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 ;) )
Поиск в любой ревизии, любые файлы:
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
Я взял ответ @Jeet и перенес его в Windows (благодаря этому ответу):
FOR /F %x IN ('"git rev-list --all"') DO @git grep <regex> %x > out.txt
Обратите внимание, что для меня, по какой-то причине, фактический коммит, который удалил это регулярное выражение, не появился в выходных данных команды, а только один коммит до него.
Для простоты я бы предложил использовать GUI: gitk - Браузер репозитория Git, он довольно гибкий
и вы можете перемещаться по результатам, используя стрелки вверх / вниз
Всякий раз, когда я нахожусь на вашем месте. Я использую следующую командную строку:
git log -S "<words/phrases i am trying to find>" --all --oneline --graph
Объяснение:
git log
- Нужно ли мне писать больше здесь, он показывает журналы в хронологическом порядке.-S "<words/phrases i am trying to find>"
- Он показывает все те git коммиты, где в любом файле (добавлен / изменен / удален) есть слова / фразы, которые я пытаюсь найти без символов "<>".--all
- Для обеспечения и поиска по всем филиалам.--oneline
- Он сжимает журнал Git в одну строку.--graph
- Создает график хронологически упорядоченных коммитов.
Для тех, кто пытается сделать это в SourceTree, в интерфейсе пользователя нет прямой команды (начиная с версии 1.6.21.0). Однако вы можете использовать команды, указанные в принятом ответе, открыв окно терминала (кнопка доступна на главной панели инструментов) и скопировав / вставив их в него.
Примечание: представление поиска SourceTree может частично выполнять поиск текста для вас. Нажмите Ctrl + 3, чтобы перейти к представлению "Поиск" (или нажмите вкладку "Поиск" внизу). В крайнем правом углу установите для параметра "Тип поиска" значение " Изменения файлов", а затем введите строку, которую хотите найти. Этот метод имеет следующие ограничения по сравнению с приведенной выше командой:
- SourceTree показывает только коммиты, которые содержат искомое слово в одном из измененных файлов. Поиск точного файла, который содержит текст поиска, снова является ручной задачей.
- RegEx не поддерживается.
Я тут немного удивился и может я пропустил тот ответ, который искал, но я пришел сюда искать поиск по главам всех веток. Не для каждой ревизии в репозитории, поэтому для меня использование
Другими словами, для меня наиболее полезным вариантом будет
git grep -i searchString $(git branch -r)
или же
git branch -r | xargs git grep -i searchString
или же
git branch -r | xargs -n1 -i{} git grep -i searchString {}
И, конечно же, здесь вы можете попробовать подход с использованием регулярных выражений. Что здорово в этом подходе, так это то, что он работает непосредственно с удаленными ветками. Мне не нужно было проверять ни одну из этих ветвей.
Добавление дополнительных к уже имеющимся ответам. Если вы знаете файл, в котором могли бы сделать это:
git log --follow -p -S 'search-string' <file-path>
--follow: выводит историю файла
Вдохновленный ответом /questions/35949221/kak-vyipolnit-poisk-poisk-zafiksirovannogo-koda-v-istorii-git/35949239#35949239 , я нашел
git grep
похоже, ищет полную базу кода при каждой фиксации, а не только различия, поэтому результат имеет тенденцию повторяться и долго. Этот скрипт ниже будет искать только различия каждого коммита git:
for commit in $(git rev-list --all); do
# search only lines starting with + or -
if git show "$commit" | grep "^[+|-].*search-string"; then
git show --no-patch --pretty=format:'%C(yellow)%h %Cred%ad %Cblue%an%Cgreen%d %Creset%s' --date=short $commit
fi
done
Пример вывода, нижний коммит git - это тот, который первым ввел изменение, которое я ищу:
csshx$ for commit in $(git rev-list --all); do
> if git show "$commit" | grep "^[+|-].*As csshX is a command line tool"; then
> git show --no-patch --pretty=format:'%C(yellow)%h %Cred%ad %Cblue%an%Cgreen%d %Creset%s' --date=short $commit
> fi
> done
+As csshX is a command line tool, no special installation is needed. It may
987eb89 2009-03-04 Gavin Brock Added code from initial release
Итак, вы пытаетесь просмотреть старые версии кода, чтобы увидеть, где что-то существует в последний раз?
Если бы я делал это, я бы использовал git bisect. Используя bisect, вы можете указать известную хорошую версию, известную плохую версию и простой скрипт, который проверяет, является ли версия хорошей или плохой (в этом случае grep, чтобы увидеть, присутствует ли код, который вы ищете). Запуск этого найдет, когда код был удален.
Хорошо, только сегодня я дважды видел людей, которые хотели более близкого эквивалента для
hg grep
, что похоже на
git log -pS
но ограничивает вывод только (аннотированными) измененными строками.
Что, я полагаю, было бы удобнее, чем
/pattern/
в пейджере, если вам нужен быстрый обзор.
Итак, вот сканер diff-hunk, который принимает
git log --pretty=%h -p
выводит и выводит аннотированные строки изменений. Положи это в
diffmarkup.l
скажем, например
make ~/bin/diffmarkup
, и используйте его как
git log --pretty=%h -pS pattern | diffmarkup | grep pattern
%option main 8bit nodefault
// vim: tw=0
%top{
#define _GNU_SOURCE 1
}
%x commitheader
%x diffheader
%x hunk
%%
char *afile=0, *bfile=0, *commit=0;
int aline,aremain,bline,bremain;
int iline=1;
<hunk>\n ++iline; if ((aremain+bremain)==0) BEGIN diffheader;
<*>\n ++iline;
<INITIAL,commitheader,diffheader>^diff.* BEGIN diffheader;
<INITIAL>.* BEGIN commitheader; if(commit)free(commit); commit=strdup(yytext);
<commitheader>.*
<diffheader>^(deleted|new|index)" ".* {}
<diffheader>^"---".* if (afile)free(afile); afile=strdup(strchrnul(yytext,'/'));
<diffheader>^"+++".* if (bfile)free(bfile); bfile=strdup(strchrnul(yytext,'/'));
<diffheader,hunk>^"@@ ".* {
BEGIN hunk; char *next=yytext+3;
#define checkread(format,number) { int span; if ( !sscanf(next,format"%n",&number,&span) ) goto lostinhunkheader; next+=span; }
checkread(" -%d",aline); if ( *next == ',' ) checkread(",%d",aremain) else aremain=1;
checkread(" +%d",bline); if ( *next == ',' ) checkread(",%d",bremain) else bremain=1;
break;
lostinhunkheader: fprintf(stderr,"Lost at line %d, can't parse hunk header '%s'.\n",iline,yytext), exit(1);
}
<diffheader>. yyless(0); BEGIN INITIAL;
<hunk>^"+".* printf("%s:%s:%d:%c:%s\n",commit,bfile+1,bline++,*yytext,yytext+1); --bremain;
<hunk>^"-".* printf("%s:%s:%d:%c:%s\n",commit,afile+1,aline++,*yytext,yytext+1); --aremain;
<hunk>^" ".* ++aline, ++bline; --aremain; --bremain;
<hunk>. fprintf(stderr,"Lost at line %d, Can't parse hunk.\n",iline), exit(1);
Ответ @Jeet работает в 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 "(^.*?:)|(:.*)", "" }
git rev-list --all | xargs -n 5 git grep EXPRESSION
это решение для @Jeet, поэтому оно показывает результаты во время поиска, а не только в конце (что может занять много времени в большом репо).
В моем случае мне нужно было найти Short Commit, и перечисленные решения, к сожалению, не работали.
Мне удалось сделать это с помощью: (заменить токен REGEX)
for commit in $(git rev-list --all --abbrev-commit)
do
if [[ $commit =~ __REGEX__ ]]; then
git --no-pager show -s --format='%h %an - %s' $commit
fi
done
Сценарий. Вы тщательно очистили свой код с помощью IDE. Проблема: IDE очистил больше, чем нужно, и теперь ваш код не скомпилирован (недостающие ресурсы и т. Д.)
Решение:
git grep --cached "text_to_find"
Он найдет файл, в котором "text_to_find" был изменен.
Теперь вы можете отменить это изменение и скомпилировать свой код.
Команда для поиска в истории git
git log -S"alter" --author="authorname" --since=2021.1.1 --until=2023.1.1 -- .
A. Полные, уникальные, отсортированные пути:
# Get all unique filepaths of files matching 'password'
# Source: https://stackoverflow.com/a/69714869/10830091
git rev-list --all | (
while read revision; do
git grep -F --files-with-matches 'password' $revision | cat | sed "s/[^:]*://"
done
) | sort | uniq
Б. Уникальные отсортированные имена файлов (не пути):
# Get all unique filenames matching 'password'
# Source: https://stackoverflow.com/a/69714869/10830091
git rev-list --all | (
while read revision; do
git grep -F --files-with-matches 'password' $revision | cat | sed "s/[^:]*://"
done
) | xargs basename | sort | uniq
Эта вторая команда полезна для BFG, потому что она принимает только имена файлов, а не репо-относительные / системные пути.
Ознакомьтесь с моим полным ответом здесь для получения дополнительных объяснений.
Другое решение для Windows и PowerShell приведено ниже:
git rev-list --all | ForEach-Object { git grep <expression> $_ }
Вам нужно заменить<expression>
с вашим регулярным выражением.