Git - як я можу переглянути історію змін методу / функції?


93

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

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

Мовою, з якою я зараз працюю, є Objective-C, а SCM, яким я зараз користуюся, є git, але мені було б цікаво знати, чи існує ця функція для будь-якої SCM / мови.


1
Я бачив таку функціональність у пропозиції Git GSoG.
Ві.

Це пропозиція, про яку ви говорили? lists-archives.org/git/…
Ерік Б


@lpapp запитання тут було поставлено 10 місяців раніше, інше питання повинно бути позначене як обман цього (якщо вони взагалі обмануті).
Dirty-flow

2
@lpapp Це два абсолютно різних запитання. Можливо, ви можете написати сценарій, який вирішує ім'я функції в діапазоні рядків, а потім використовувати техніку з цього питання, щоб отримати історію цих рядків, але сам по собі він не відповідає на це питання.
Ерік Б,

Відповіді:


100

Останні версії git logвивчили спеціальну форму -Lпараметра:

-L: <ім'я функції>: <файл>

Простежте розвиток діапазону рядків, заданого "<start>,<end>"(або регулярним виразом імені функції <funcname>) в межах <file>. Ви не можете надати жодних обмежувачів специфікації шляху. Наразі це обмежується прогулянкою, починаючи з однієї редакції, тобто ви можете навести лише нуль або один позитивний аргумент версії. Ви можете вказати цю опцію кілька разів.
...
Якщо “:<funcname>”вказано замість <start>і <end>, це регулярний вираз, що позначає діапазон від першого відповідного рядка функції <funcname>до наступного рядка функції. “:<funcname>”здійснює пошук з кінця попереднього -Lдіапазону, якщо такий є, інакше з початку файлу. “^:<funcname>”пошук з початку файлу.

Іншими словами: якщо ви попросите Git git log -L :myfunction:path/to/myfile.c, він тепер із задоволенням надрукує історію змін цієї функції.


16
це може працювати для цільового c нестандартно, але якщо ви робите це для інших мов (наприклад, Python, Ruby тощо), можливо, вам доведеться додати відповідну конфігурацію всередині файлу .gitattributes, щоб git розпізнавав функцію / метод декларації цією мовою. Для python використовуйте * .py diff = python, для ruby ​​використовуйте * .rb diff = ruby
samaspin

1
Як git відстежує функцію?
nn0p

@ nn0p Я припускаю, маючи знання синтаксису кількох мов і знаючи, таким чином, як виділити функцію та простежити її зміни.
JasonGenX

4
Розширюючи коментар @ samaspin, для інших мов ви можете звернутися до документів тут: git-scm.com/docs/gitattributes#_generating_diff_text
edhgoose

Це не працює для таких мов, як Scala та Java і навіть C, які використовують вкладені фігурні дужки (регулярні вирази в цілому не справляються з цим), і навіть початкова, кінцева форма не працює, коли функція переміщується у файл і модифіковані в тому ж коміті.
Робін Грін,

16

Використання 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"
    
  • Визначте textconvфільтр Git для вашого нового сценарію. (Дивgitattributes сторінці довідки.) Ім'я фільтра та розташування команди можуть бути будь-якими.

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

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

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

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

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

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


1
Взяти більше одного аргументу мені не вдалося, але розміщення назви функції в жорстких роботах працює нормально, і це дійсно чудово!
qwertzguy

Блискуче, хоча мені цікаво, наскільки (не) зручно перемикатися між безліччю різних методів. Крім того, чи знаєте ви про програму, яка може витягнути цілу С-подібну функцію?
nafg

12

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

5
Я не запускав команду, але мені здається, що ця команда відображатиме лише коміти, які торкаються рядка, що відповідає регулярному виразу, що не є цілим методом / функцією.
Ерік Б

якщо ви хочете отримати більше контексту, ви можете замінити --onelineна-p
lfender6445

3
Але що, якби в метод було внесено 20 рядків?
nafg

+1. Для мене найкраща відповідь спрацювала, але показала лише найновіший коміт. Можливо через перебазування або функцію, яка рухається приблизно кілька разів, не впевнений. Шукаючи фактичний рядок коду замість функції, в якій він був (хоча і однорядковий), ця відповідь дозволила мені легко виявити коміт, який я шукав. На щастя, я зазвичай пишу корисні повідомлення про коміти!
Dave S,

11

Найближче, що ви можете зробити, це визначити позицію вашої функції у файлі (наприклад, скажімо, ваша функція i_am_buggyзнаходиться в рядках 241-263 of 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

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


Солодко! FYI, це нове у Git v1.8.4. (Мабуть, мені слід оновити.) Хоча було б більш точне рішення, якби хтось написав сценарій відповіді Пола Уіттейкера.
Грег Прайс

@GregPrice Очевидно, краї пошуку можуть бути навіть регулярними виразами , так що ви можете мати принаймні більш-менш точну початкову точку.
badp

Ух ти. Насправді: замість того, щоб писати власний регулярний вираз, ви можете просто сказати -L ":int myfunc:foo/bar.c"і обмежити функцію з таким ім’ям. Це фантастика - дякую за вказівник! Тепер, якби виявлення функцій було трохи надійнішим ...
Грег Прайс

3

Правильний спосіб - використовувати, 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

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


4
за допомогою git gui blameви можете переходити до старих версій.
Ві.

0
  1. Показати історію функцій за допомогою, git log -L :<funcname>:<file>як показано у відповіді eckes та git doc

    Якщо він нічого не показує, зверніться до Визначенню призначеного для користувача ханко-заголовка , щоб додати що - щось на зразок *.java diff=javaв .gitattributes файл для підтримки мови.

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

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

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