Git - как просмотреть историю изменений метода/функции?

Итак, я нашел вопрос о том, как просматривать историю изменений файла, но история изменений этого файла огромна, и меня действительно интересуют только изменения конкретного метода. Так можно ли увидеть историю изменений только для этого конкретного метода?

Я знаю, что для анализа кода потребуется 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

  1. Показать историю функций с помощью git log L :<funcname>:<file>, как показано в ответах eckes и git doc

    Если он ничего не показывает, см. Определение настраиваемого заголовка hunk, чтобы добавить что-то вроде *.java diff=java в файл .gitattributes для поддержки вашего языка.

  2. Показать историю функций между коммитами с помощью git log commit1..commit2 -L :functionName:filePath

  3. Показать историю перегруженных функций (может быть много функций с одинаковыми именами, но с разными параметрами) с помощью git log -L :sum\(double:filepath