Як зібрати (шукати) скоєний код в історії Git


1433

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

Дуже поганим рішенням є обклеювання журналу:

git log -p | grep <pattern>

Однак це не повертає хеш-файли відразу. Я грав навколо, git grepбезрезультатно.


2
Ці повідомлення в блозі Хуніо С Хамано (сервісне обслуговування git) можуть бути цікавими для вас: * Кінцевий інструмент відстеження контенту Лінуса (про пошук git log -Sпікакси, тобто про вину) * [Весело з "git log --grep"] [2] (пошук повідомлень про фіксацію ) * [Fun with "git grep"] [3] [2]: gitster.livejournal.com/30195.html [3]: gitster.livejournal.com/27674.html
Якуб Нарбський

4
можливий дублікат програми « Як зафіксувати git» для певного слова

відповідь від можливого дубліката насправді працює: stackoverflow.com/a/1340245/492
CAD блокувати

Проблема з цим полягає в тому, що це не дає жодних контекстів для зміни. Тобто хто / коли
Sonic Soul

Відповіді:


1890

Щоб шукати вміст фіксації (тобто фактичні рядки джерела, на відміну від повідомлень про фіксацію тощо), потрібно зробити:

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(навіть якщо в цьому редакції не було змін цього файлу).

Ось деякі інші корисні способи пошуку вашого джерела:

Шукайте в робочому дереві текст, що відповідає регулярному вираженню регулярного вираження:

git grep <regexp>

Шукайте в робочому дереві рядки тексту, що відповідають регулярному виразу regexp1 або regexp2:

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

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

git grep -l -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>)

61
Дякую, чудово працює! Сумно, хоча, що "$ (git rev-list - all)" потрібен і не зручний перемикач, щоб вказати пошук у всій історії гілки.
Ортвін Генц

3
Відмінно. +1. GitBook додає деякі деталі ( book.git-scm.com/4_finding_with_git_grep.html ), а Хуніо С Хамано ілюструє деякі ваші моменти: gitster.livejournal.com/27674.html
VonC,

18
На жаль, я не можу досягти цього з msysgit-1.7.4. Це мені каже sh.exe": /bin/git: Bad file number. Відповідь VonC також працює з msysgit.
eckes

4
Якщо ви отримаєте помилку "не в змозі прочитати дерево", коли ви викликаєте історію git grep за допомогою списку rev, можливо, вам доведеться очистити речі. Спробуйте git gcабо перевірити: stackoverflow.com/questions/1507463/…
Ентоні Паноццо

8
Так, на жаль, це теж не вдалося в Windows.
mlissner

551

Слід скористатися опцією pickaxe ( -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 регулярний вираз замість пошуку рядка. Приклад (від git log):git log -S"frotz\(nitfol" --pickaxe-regex


Як прокоментував Роб , цей пошук враховує регістри - він відкрив подальше запитання про те, як шукати невідчутну до регістру справу.


3
Дякую, я не знав про цей варіант. Схоже, це найкраще рішення, якщо вас цікавлять повідомлення про фіксацію, а рішення Jeet є найбільш підходящим, якщо вам потрібна традиційна поведінка греппінгу UNIX у чистому співпаданні рядків.
Ортвін Генц

@Ortwin: погодився (і я підтримав обране рішення). git logтрохи в вашому питанні змусив мене плутати;)
VonC

12
Поєднайте його з -pпрапором, щоб також вивести розл.
Сандер

Чи є спосіб виключити всі каталоги, що відповідають певним шаблонам, використовуючи git log -S?
BakaKuna

3
@Anentropic вам знадобляться --branches --allваріанти для пошуку всіх репо.
VonC

249

Мій улюблений спосіб зробити це за допомогою параметра git logs -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

Оскільки кількість разів "привіт" у файлі є однаковим до та після виконання цього зобов'язання, воно не збігається з використанням -Shello. Однак, оскільки відбулася зміна відповідності рядків hello, фіксація буде показана з використанням -Ghello.


2
Чи є спосіб відобразити відповідний контекст зміни у виході журналу git?
Тіло-Олександр Гінкель

13
@ Thilo-AlexanderGinkel - я зазвичай просто додаю -pопцію, щоб показати різницю для кожної комісії. Потім, коли журнал відкривається в моєму пейджері, я шукаю все, що він шукає. Якщо ваш пейджер є lessі ви git log -Ghello -p, ви можете набрати /hello, натиснути Enterта використовувати nта Nзнайти наступне / попереднє виникнення "привіт".
Тайлер Холієн

Я виявив цікаву проблему з -Gі Regex: Якщо командний рядок використовує UTF-8, а файл, який ви шукаєте, використовує деякі ISO-латинські (8 бітні) кодування, .*виходить з ладу. Наприклад, у мене є зміна Vierter Entwurf-> Fünfter Entwurf, і хоча 'V.*ter Entwurf'виробляє відповідність, 'F.*ter Entwurf'не робить.
У. Віндл

51

Якщо ви хочете переглянути зміни коду (подивіться, що насправді було змінено даним словом за всю історію), перейдіть до 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 ;) )

11
Прийняте рішення не працює для мене ні git log -S. Цей зробив!
rodvlopes

29

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

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

Цей список команд журналу робить комісії, які додають або видаляють задану пошукову рядок / регулярний вираз, (як правило, спочатку останній. Цей -pпараметр призводить до того, що відповідна різниця відображатиметься там, де шаблон був доданий чи видалений, тому ви можете бачити його в контексті.

Знайшовши відповідний документ, який додає шуканий текст (наприклад, 8beeff00d), знайдіть гілки, які містять коміти:

git branch -a --contains 8beeff00d

Привіт, ці рядки, здається, зовсім не працюють. Моя команда:> git log -p --all -S 'public string DOB {get; набір; } = рядок.Порожня; ' і кожного разу, коли я намагаюся запустити його, я отримую> fatal: неоднозначний аргумент 'string': невідома редакція або шлях не в робочому дереві. > Використовуйте '-', щоб відокремити шляхи від редакцій, наприклад:> 'git <command> [<revision> ...] - [<file> ...]'
користувач216652

@ user216652 Чомусь 'котирування не групують пошукову рядок як єдиний аргумент. Натомість, 'publicце аргумент -S, а решта трактує як окремі аргументи. Я не впевнений, у якому середовищі ти працюєш, але цей контекст був би необхідний для усунення несправностей. Я б запропонував відкрити окремий запит StackOverflow, якщо це потрібно, щоб допомогти вам усунути неполадки, з усім контекстом того, як ваша команда git надсилається до оболонки. Мені здається, що це надсилається через якусь іншу команду? Коментарі тут не є правильним місцем для цього.
Едвард Андерсон

26

Я взяв відповідь Jeet і адаптував її до Windows (завдяки цій відповіді ):

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

Зауважте, що для мене чомусь фактична фіксація, яка видалила цей регулярний вираз, з'явилася не у виведенні команди, а скоріше однією фіксацією до неї.


2
+1 - і якщо ви хочете уникнути потрапляння "q" після кожної знахідки, додайте --no-pagerдо команди git наприкінці
cgp

2
Також зауважу, що додавання до текстового файлу має додаткову перевагу в тому, що насправді відображається текст, що відповідає. (додайте до текстового файлу, використовуючи >>results.txtдля тих, хто не розбирається в трубопроводах Windows ...
cgp

1
І я подумав, що синтаксис
Баша

23

Шукати в будь-якій редакції, будь-якому файлі :

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

11

Для простоти я б запропонував використовувати GUI: gitk - браузер репозиторію Git . Це досить гнучко

  1. Для пошуку коду:

    Введіть тут опис зображення
  2. Для пошуку файлів:

    Введіть тут опис зображення
  3. Звичайно, він також підтримує регулярні вирази:

    Введіть тут опис зображення

І ви можете орієнтуватися по результатах за допомогою стрілок вгору / вниз.


6

Для тих, хто намагається це зробити в Sourcetree , в інтерфейсі для нього немає прямої команди (станом на версію 1.6.21.0). Однак ви можете використовувати команди, зазначені у прийнятій відповіді, відкривши вікно терміналу (кнопка доступна на головній панелі інструментів) та скопіювати / вставити їх у них.

Примітка: Sourcetree в Пошуку вид може частково шукати текст для вас. Натисніть Ctrl+, 3щоб перейти до перегляду пошуку (або клацніть на вкладці Пошук, доступній внизу). З правого краю встановіть тип пошуку на " Зміни файлів", а потім введіть рядок, який потрібно шукати. Цей метод має такі обмеження порівняно з вищевказаною командою:

  1. Sourcetree тільки показує коммітов , що містять шукане слово в одному з змінених файлів. Пошук точного файлу, що містить текст пошуку, - знову ж таки ручне завдання.
  2. RegEx не підтримується.

4

Щоразу, коли опиняюся у вас, я використовую такий командний рядок:

git log -S "<words/phrases i am trying to find>" --all --oneline  --graph

Пояснення:

  1. git log- Потрібно, щоб я тут більше писав; він показує журнали в хронологічному порядку.
  2. -S "<words/phrases i am trying to find>" - Він показує всі ті Git-зобов'язання, де будь-який файл (доданий / модифікований / видалений) містить слова / фрази, які я намагаюся знайти без символів '<>'.
  3. --all - Примусовий пошук і пошук у всіх відділеннях.
  4. --oneline - Він стискає журнал Git в одному рядку.
  5. --graph - Це створює графік хронологічно упорядкованих комітетів.

1
"Кожного разу, коли я опиняюся у вас, я відчуваю необхідність використовувати git!"
Sebi

1
Це чудова відповідь!
Альф Ітон

@AlfEaton моє задоволення!
surajs1n

2

Відповідь 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 "(^.*?:)|(:.*)", "" }

1

Отже, ви намагаєтеся проглядати старіші версії коду, шукаючи, де щось останнє існує?

Якби я робив це, я б, ймовірно, використовував біт-біт . Використовуючи bisect, ви можете вказати відому хорошу версію, відому погану версію та простий скрипт, який перевіряє, чи версія хороша чи погана (у цьому випадку є бажання, щоб побачити, чи є код, який ви шукаєте, присутній ). Запустивши це, ви знайдете, коли код було видалено.


2
Так, але ваш "тест" може бути скриптом, який вказує на код і повертає "true", якщо код існує, і "false", якщо цього немає.
Роб Ді Марко

2
Що робити, якщо код був поганий у версії 10, став хорошим у редакції 11 та знову став поганим у редакції 15 ...
Паоло

2
Я згоден з Паоло. Двійковий пошук підходить лише для "впорядкованих" значень. Що стосується git bisect, це означає, що всі "хороші" зміни переходять до всіх "поганих" змін, починаючи з опорної точки, але це припущення не може бути зроблене при пошуку перехідного коду. Це рішення може працювати в деяких випадках, але це не дуже вдале загальне рішення.
Кент

Я думаю, що це дуже неефективно, оскільки все дерево перевіряється кілька разів на бісект.
У. Віндл

0

Сценарій: Ви очистили свій код за допомогою свого IDE. Проблема: IDE очищено більше, ніж слід, і тепер ви не збираєте код (відсутні ресурси тощо)

Рішення:

git grep --cached "text_to_find"

Він знайде файл, де "text_to_find" було змінено.

Тепер ви можете скасувати цю зміну і скласти свій код.


0
git rev-list --all | xargs -n 5 git grep EXPRESSION

це коригування рішення Jeet , тому воно показує результати під час пошуку, а не лише в кінці (що може тривати довгий час у великому сховищі).


-1

У моєму випадку мені потрібно було знайти короткий фіксатор, і перераховані рішення, на жаль, не працювали.

Мені вдалося це зробити (замінити маркер 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.