Итак, я нашел вопрос о том, как просматривать историю изменений файла, но история изменений этого файла огромна, и меня действительно интересуют только изменения конкретного метода. Так можно ли увидеть историю изменений только для этого конкретного метода?
Я знаю, что для анализа кода потребуется git, и анализ будет различным для разных языков, но декларации методов/функций выглядят очень похожими на большинстве языков, поэтому я подумал, что, возможно, кто-то реализовал эту функцию.
Язык, с которым я сейчас работаю, - Objective-C, и SCM, который я использую в настоящее время, - это git, но мне было бы интересно узнать, существует ли эта функция для любого SCM/языка.
Ответ 1
В последних версиях git log
вы узнали особую форму параметра -L
:
-L: <funcname> : <file>
Проследите эволюцию диапазона строк, заданного "<start>,<end>"
(или имя регулярного выражения <funcname>
) в пределах <file>
. Вы не можете указывать ограничители пути. В настоящее время это ограничивается прогулкой, начиная с одной ревизии, т.е. Вы можете дать только нулевые или одни положительные аргументы ревизии. Вы можете указать эту опцию несколько раз....
Если вместо <start>
и <end>
задано ":<funcname>"
, это регулярное выражение, которое обозначает диапазон от первой строки funcname, которая соответствует <funcname>
, вплоть до следующей строки funcname. ":<funcname>"
выполняет поиск с конца предыдущего диапазона -L
, если таковой имеется, в противном случае от начала файла. "^:<funcname>"
выполняет поиск с начала файла.
Другими словами: если вы спросите Git к git log -L :myfunction:path/to/myfile.c
, он теперь с радостью распечатает историю изменений этой функции.
Ответ 2
Использование git gui blame
трудно использовать в скриптах, а пока git log -G
и git log --pickaxe
могут показать вам, когда определение метода появилось или исчезло, я не нашел способа сделать их перечислить все изменения сделанные в теле вашего метода.
Однако вы можете использовать свойство gitattributes
и textconv
, чтобы собрать вместе решение, которое делает именно это. Хотя эти функции изначально были предназначены, чтобы помочь вам работать с двоичными файлами, они также работают здесь.
Ключ должен иметь Git удалить из файла все строки, кроме тех, которые вам интересны, перед выполнением каких-либо операций diff. Тогда git log
, git diff
и т.д. Будут видеть только интересующую вас область.
Здесь очертания того, что я делаю на другом языке; вы можете настроить его для своих нужд.
-
Напишите короткую оболочку script (или другую программу), которая принимает один аргумент - имя исходного файла и выводит только интересную часть этого файла (или ничего, если ни одно из них не интересно). Например, вы можете использовать sed
следующим образом:
#!/bin/sh
sed -n -e '/^int my_func(/,/^}/ p' "$1"
-
Определите фильтр Git textconv
для нового script. (Подробнее см. Справочную страницу gitattributes
.) Имя фильтра и расположение команды могут быть любыми, что вам нравится.
$ git config diff.my_filter.textconv /path/to/my_script
-
Сообщите Git использовать этот фильтр перед вычислением diff для соответствующего файла.
$ echo "my_file diff=my_filter" >> .gitattributes
-
Теперь, если вы используете -G.
(обратите внимание на .
), чтобы перечислить все коммиты, которые производят видимые изменения при применении фильтра, у вас будут именно те коммиты, которые вас интересуют. другие параметры, которые используют процедуры Git diff, такие как --patch
, также получат это ограниченное представление.
$ git log -G. --patch my_file
-
Вуаля!
Одно полезное усовершенствование, которое вы, возможно, захотите сделать, - это чтобы ваш фильтр script принял имя метода в качестве своего первого аргумента (а второй - файла). Это позволяет вам указать новый интересующий метод, просто позвонив git config
, вместо необходимости редактировать ваш script. Например, вы можете сказать:
$ git config diff.my_filter.textconv "/path/to/my_command other_func"
Конечно, фильтр script может делать все, что вам нравится, принимать больше аргументов или что-то еще: существует большая гибкость, кроме того, что я здесь показал.
Ответ 3
В git log есть опция '-G', чтобы найти все различия.
-G Ищите различия, чья добавленная или удаленная строка соответствует учитывая <regex>
.
Просто дайте ему правильное регулярное выражение имени функции, о которой вы заботитесь. Например,
$ git log --oneline -G'^int commit_tree'
40d52ff make commit_tree a library function
81b50f3 Move 'builtin-*' into a 'builtin/' subdirectory
7b9c0a6 git-commit-tree: make it usable from other builtins
Ответ 4
Самое близкое, что вы можете сделать, это определить положение вашей функции в файле (например, скажем, ваша функция i_am_buggy
находится в строках 241-263 из foo/bar.c
), а затем запустите что-то в результате:
git log -p -L 200,300:foo/bar.c
Это откроет меньше (или эквивалентный пейджер). Теперь вы можете ввести /i_am_buggy
(или ваш эквивалент пейджера) и начать выполнять изменения.
Это может даже работать, в зависимости от стиля вашего кода:
git log -p -L /int i_am_buggy\(/,+30:foo/bar.c
Это ограничивает поиск с первого попадания этого регулярного выражения (в идеале объявление функции) до тридцати строк после этого. Аргумент end также может быть регулярным выражением, хотя обнаружение того, что с regexp является предложением iffier.
Ответ 5
Правильный способ - использовать git log -L :function:path/to/file
, как описано в ответе eckes.
Но кроме того, если ваша функция очень длинная, вы можете захотеть видеть только изменения, внесенные различными коммитами, а не целые строки функций, включая неизмененные, для каждого коммита, который может касаться только одной из этих строк. Как и обычный diff
.
Обычно git log
может просматривать различия с -p
, но это не работает с -L
.
Таким образом, вы должны grep
git log -L
показывать только задействованные строки и коммиты/заголовки файлов для их контекстуализации. Хитрость здесь в том, чтобы сопоставить только терминальные цветные линии, добавив переключатель --color
с регулярным выражением. Наконец:
git log -L :function:path/to/file --color | grep --color=never -E -e "^(^[\[[0-9;]*[a-zA-Z])+" -3
Обратите внимание, что ^[
должен быть действительным, буквальным ^[
. Вы можете ввести их, нажав ^ V ^ [в bash, то есть Ctrl + V, Ctrl + [. Ссылка здесь.
Также последний переключатель -3
позволяет печатать 3 строки выходного контекста, до и после каждой совпавшей строки. Вы можете настроить его под свои нужды.
Ответ 6
git винить показывает, кто последний изменил каждую строку файла; вы можете указать строки для проверки, чтобы избежать получения строк строк вне вашей функции.
Ответ 7
Показать историю функций с помощью git log L :<funcname>:<file>
, как показано в ответах eckes и git doc
Если он ничего не показывает, см. Определение настраиваемого заголовка hunk, чтобы добавить что-то вроде *.java diff=java
в файл .gitattributes
для поддержки вашего языка.
Показать историю функций между коммитами с помощью git log commit1..commit2 -L :functionName:filePath
Показать историю перегруженных функций (может быть много функций с одинаковыми именами, но с разными параметрами) с помощью git log -L :sum\(double:filepath