Git - как я могу просмотреть историю изменений метода / функции? - PullRequest
73 голосов
/ 24 января 2011

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

Я знаю, что для анализа кода потребуется git, и анализ будет отличаться для разных языков, но объявления методов / функций выглядят очень похоже на большинстве языков, поэтому я подумал, что, возможно, кто-то реализовал эту функцию.

Язык, с которым я сейчас работаю, это Objective-C, а SCM, который я сейчас использую, это git, но мне было бы интересно узнать, существует ли эта функция для какого-либо языка SCM / *.

Ответы [ 6 ]

68 голосов
/ 27 ноября 2015

Последние версии git log изучили специальную форму параметра -L:

-L: :

Отслеживание эволюции диапазона линии, заданного "<start>,<end>" (или именем функции regex <funcname>) в пределах <file>.Вы не можете давать какие-либо ограничения пути.В настоящее время это ограничивается прогулкой, начинающейся с одной ревизии, т. Е. Вы можете указать только один или несколько положительных аргументов ревизии.Вы можете указать эту опцию более одного раза.
...
Если вместо <start> и <end> задано “:<funcname>”, это регулярное выражение, которое обозначает диапазон из первой строки funcname, котораясоответствует <funcname>, до следующей строки funcname.“:<funcname>” выполняет поиск с конца предыдущего диапазона -L, если таковой имеется, в противном случае с начала файла.“^:<funcname>” поиск с начала файла.

Другими словами: если вы попросите Git набрать git log -L :myfunction:path/to/myfile.c, он теперь с радостью распечатает историю изменений этой функции.

15 голосов
/ 05 июня 2013

Использование git gui blame сложно использовать в сценариях, и хотя git log -G и git log --pickaxe каждый может показать вам, когда определение метода появилось или исчезло, я не нашел способа заставить их перечислить все изменения сделано для тела вашего метода.

Однако вы можете использовать gitattributes и свойство textconv, чтобы собрать решение, которое именно это и делает. Хотя эти функции изначально были предназначены для того, чтобы помочь вам работать с двоичными файлами, они работают и здесь.

Ключ состоит в том, чтобы Git удалял из файла все строки, кроме тех, которые вас интересуют, перед выполнением любых операций сравнения. Тогда git log, git diff и т. Д. Увидят только интересующую вас область.

Вот схема того, что я делаю на другом языке; Вы можете настроить его для своих собственных нужд.

  • Напишите короткий сценарий оболочки (или другую программу), который принимает один аргумент - имя исходного файла - и выводит только интересную часть этого файла (или ничего, если ни один из них не интересен). Например, вы можете использовать sed следующим образом:

    #!/bin/sh
    sed -n -e '/^int my_func(/,/^}/ p' "$1"
    
  • Определите фильтр Git textconv для вашего нового скрипта. (Для получения дополнительной информации см. Справочную страницу gitattributes.) Имя фильтра и расположение команды могут быть любыми.

    $ git config diff.my_filter.textconv /path/to/my_script
    
  • Скажите Git использовать этот фильтр перед вычислением diff для рассматриваемого файла.

    $ echo "my_file diff=my_filter" >> .gitattributes
    
  • Теперь, если вы используете -G. (обратите внимание на .), чтобы перечислить все коммиты, которые приводят к видимым изменениям при применении вашего фильтра, у вас будут именно те коммиты, которые вас интересуют. Любой другие опции, использующие процедуры сравнения Git, такие как --patch, также получат это ограниченное представление.

    $ git log -G. --patch my_file
    
  • Вуаля!

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

$ git config diff.my_filter.textconv "/path/to/my_command other_func"

Конечно, скрипт фильтра может делать все что угодно, принимать больше аргументов или что-то еще: есть большая гибкость, помимо того, что я здесь показал.

10 голосов
/ 05 сентября 2011

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
9 голосов
/ 07 февраля 2014

Самое близкое, что вы можете сделать, это определить положение вашей функции в файле (например, скажем, ваша функция 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

Это ограничивает поиск от первого обращения к этому регулярному выражению (в идеале до объявления вашей функции) до тридцати строк после этого. Конечный аргумент также может быть регулярным выражением, хотя обнаружение , что с регулярным выражением, является более простым предложением.

3 голосов
/ 25 мая 2018

Правильный способ - использовать 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 строки выходного контекста, до и после каждой совпавшей строки.Вы можете настроить его под свои нужды.

2 голосов
/ 24 января 2011

git blame показывает, кто последний раз изменял каждую строку файла;Вы можете указать строки для проверки, чтобы избежать попадания истории строк вне вашей функции.

...