Git - jak wyświetlić historię zmian metody / funkcji?

93

Więc znalazłem pytanie, jak wyświetlić historię zmian pliku, ale historia zmian tego konkretnego pliku jest ogromna i tak naprawdę interesują mnie tylko zmiany konkretnej metody. Czy byłoby możliwe wyświetlenie historii zmian tylko dla tej konkretnej metody?

Wiem, że wymagałoby to gita do przeanalizowania kodu i że analiza byłaby inna dla różnych języków, ale deklaracje metod / funkcji wyglądają bardzo podobnie w większości języków, więc pomyślałem, że może ktoś zaimplementował tę funkcję.

Językiem, z którym obecnie pracuję, jest Objective-C, a SCM, którego obecnie używam, to git, ale chciałbym wiedzieć, czy ta funkcja istnieje dla dowolnego SCM / języka.

Erik B.
źródło
1
Widziałem taką funkcjonalność w propozycji Git GSoG.
Vi.
Czy to jest propozycja, o której mówiłeś? list-archives.org/git/…
Erik B
@lpapp pytanie tutaj zostało zadane 10 miesięcy wcześniej, drugie pytanie należy oznaczyć jako podrobione (jeśli w ogóle są oszustami).
Dirty-flow
2
@lpapp To są dwa zupełnie różne pytania. Możesz prawdopodobnie napisać skrypt, który tłumaczy nazwę funkcji na zakres wierszy, a następnie użyć techniki z tego pytania, aby uzyskać historię tych wierszy, ale sam w sobie nie odpowiada na to pytanie.
Erik B

Odpowiedzi:

100

Najnowsze wersje git lognauczyły się specjalnej postaci -Lparametru:

-L: <funcname>: <file>

Prześledź ewolucję zakresu linii podanego przez "<start>,<end>"(lub wyrażenie regularne nazwy funkcji <funcname>) w ramach <file>. Nie możesz podawać żadnych ograniczników ścieżki. Obecnie jest to ograniczone do przechodzenia od pojedynczej wersji, tj. Można podać tylko zero lub jeden dodatni argument rewizji. Możesz określić tę opcję więcej niż raz.
...
Jeśli “:<funcname>”podano zamiast <start>i <end>, jest to wyrażenie regularne określające zakres od pierwszej pasującej linii funcname <funcname>, aż do następnej linii funcname. “:<funcname>”wyszukuje od końca poprzedniego -Lzakresu, jeśli istnieje, w przeciwnym razie od początku pliku. “^:<funcname>”wyszukuje od początku pliku.

Innymi słowy: jeśli poprosisz o to Gita git log -L :myfunction:path/to/myfile.c, teraz z radością wydrukuje historię zmian tej funkcji.

eckes
źródło
16
to może zadziałać dla objective-c po wyjęciu z pudełka, ale jeśli robisz to dla innych języków (np. Python, Ruby itp.), może być konieczne dodanie odpowiedniej konfiguracji w pliku .gitattributes, aby git rozpoznał funkcję / metodę deklaracje w tym języku. Do stosowania Pythona * .py diff = python, ruby do użytku * .rb diff = rubin
samaspin
1
Jak git śledzi tę funkcję?
nn0p
@ nn0p Zakładam, że mam znajomość składni kilku języków, a tym samym wiem, jak wyodrębnić funkcję i śledzić jej zmiany.
JasonGenX
4
Rozszerzając komentarz @ samaspin, dla innych języków możesz odwołać się do dokumentów tutaj: git-scm.com/docs/gitattributes#_generating_diff_text
edhgoose
To nie działa dla języków takich jak Scala i Java, a nawet C, które używają zagnieżdżonych nawiasów klamrowych (wyrażenia regularne nie mogą tego w ogóle obsłużyć), a nawet forma początkowa i końcowa nie działa, gdy funkcja jest przenoszona w pliku i zmodyfikowane w tym samym zatwierdzeniu.
Robin Green
16

Korzystanie git gui blamejest trudne do wykorzystania w skryptach, a jednocześnie git log -Gi git log --pickaxekażdy może pokazać, kiedy pojawiła się definicja metoda lub zniknął, nie znalazłem żadnego sposobu, aby je wymienić wszystkie zmiany dokonane w ciele swojej metody.

Możesz jednak użyć gitattributesi textconvwłaściwości, aby złożyć w całość rozwiązanie, które właśnie to robi. Chociaż te funkcje były pierwotnie przeznaczone do pomocy w pracy z plikami binarnymi, działają tutaj równie dobrze.

Kluczem jest to, aby Git usunął z pliku wszystkie linie z wyjątkiem tych, którymi jesteś zainteresowany, przed wykonaniem jakichkolwiek operacji porównywania. Następnie git log, git diffitp będą widzieć tylko obszar, który Cię interesuje.

Oto zarys tego, co robię w innym języku; możesz go dostosować do własnych potrzeb.

  • Napisz krótki skrypt powłoki (lub inny program), który pobiera jeden argument - nazwę pliku źródłowego - i wyświetla tylko interesującą część tego pliku (lub nic, jeśli żaden z nich nie jest interesujący). Na przykład możesz użyć sedw następujący sposób:

    #!/bin/sh
    sed -n -e '/^int my_func(/,/^}/ p' "$1"
    
  • Zdefiniuj textconvfiltr Git dla nowego skryptu. (Zobacz gitattributesstronę podręcznika po więcej szczegółów.) Nazwa filtru i lokalizacja polecenia mogą być dowolne.

    $ git config diff.my_filter.textconv /path/to/my_script
    
  • Powiedz Gitowi, aby użył tego filtru przed obliczeniem różnic dla danego pliku.

    $ echo "my_file diff=my_filter" >> .gitattributes
    
  • Teraz, jeśli używasz -G.(uwaga .) notować wszystkie rewizje, które wywołują widocznych zmian, gdy filtr jest stosowany, trzeba będzie dokładnie te rewizje, że jesteś zainteresowany. Wszelkie inne opcje, które wykorzystują procedury diff git, takich jak --patch, będzie również ten ograniczony widok.

    $ git log -G. --patch my_file
    
  • Voilà!

Jednym z przydatnych ulepszeń, które możesz chcieć zrobić, jest ustawienie przez skrypt filtru nazwy metody jako pierwszego argumentu (i pliku jako drugiego). Pozwala to określić nową interesującą metodę tylko przez wywołanie git config, zamiast konieczności edytowania skryptu. Na przykład możesz powiedzieć:

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

Oczywiście skrypt filtrujący może robić, co chcesz, przyjmować więcej argumentów lub cokolwiek: poza tym, co tutaj pokazałem, jest dużo elastyczności.

Paul Whittaker
źródło
1
Nie udało mi się przyjąć więcej niż jednego argumentu, ale umieszczenie nazwy funkcji w ciężkiej pracy działa dobrze i jest naprawdę niesamowite!
qwertzguy
Świetnie, chociaż zastanawiam się, jak (nie) wygodne jest przełączanie się między wieloma różnymi metodami. Czy znasz program, który może wyciągnąć całą funkcję podobną do C?
nafg
12

git log ma opcję „-G”, która może być użyta do znalezienia wszystkich różnic.

-G Poszukaj różnic, których dodana lub usunięta linia pasuje do podanej <regex>.

Po prostu nadaj mu odpowiednie wyrażenie regularne nazwy funkcji, na której Ci zależy. Na przykład,

$ 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
lht
źródło
5
Nie uruchomiłem polecenia, ale wygląda na to, że to polecenie pokaże tylko zatwierdzenia, które dotykają wiersza pasującego do wyrażenia regularnego, co nie jest całą metodą / funkcją.
Erik B,
jeśli chcesz więcej kontekstu, można zastąpić --onelinez-p
lfender6445
3
Ale co, jeśli w metodzie wprowadzono zmianę w 20 liniach?
nafg
+1. Dla mnie górna odpowiedź zadziałała, ale pokazała tylko najnowsze zatwierdzenie. Prawdopodobnie z powodu podbicia lub funkcji poruszającej się kilka razy, nie jestem pewien. Wyszukując właściwy wiersz kodu zamiast funkcji, w której się znajdował (aczkolwiek jednowierszowy), ta odpowiedź pozwoliła mi łatwo znaleźć zatwierdzenie, którego szukałem. Na szczęście zazwyczaj piszę przydatne komunikaty o zmianach!
Dave S
11

Najbliższą rzeczą, jaką możesz zrobić, jest określenie pozycji Twojej funkcji w pliku (np. Powiedzmy, że twoja funkcja i_am_buggyznajduje się w wierszach 241-263 foo/bar.c), a następnie uruchom coś, co spowoduje:

git log -p -L 200,300:foo/bar.c

Spowoduje to otwarcie mniej (lub równoważnego pagera). Teraz możesz wpisać /i_am_buggy(lub odpowiednik pagera) i rozpocząć przechodzenie przez zmiany.

To może nawet zadziałać, w zależności od twojego stylu kodu:

git log -p -L /int i_am_buggy\(/,+30:foo/bar.c

Ogranicza to wyszukiwanie od pierwszego trafienia tego wyrażenia regularnego (najlepiej deklaracji funkcji) do kolejnych trzydziestu wierszy. Argumentem końcowym może być również wyrażenie regularne , chociaż wykrycie tego za pomocą wyrażenia regularnego jest bardziej niepewne.

badp
źródło
Słodkie! FYI, to nowość w Git v1.8.4. (Chyba powinienem dokonać aktualizacji.) Chociaż bardziej precyzyjne rozwiązanie byłoby fajne ... jak gdyby ktoś napisał scenariusz odpowiedzi Paula Whittakera.
Greg Price,
@GregPrice Najwyraźniej krawędzie wyszukiwania mogą być nawet wyrażeniami regularnymi , więc możesz przynajmniej mieć mniej lub bardziej precyzyjny punkt początkowy.
badp
Och, wow. W rzeczywistości: zamiast pisać własne wyrażenie regularne, możesz po prostu powiedzieć -L ":int myfunc:foo/bar.c"i ograniczyć do funkcji o tej nazwie. To fantastyczne - dzięki za wskazówkę! Gdyby tylko wykrywanie funkcji było trochę bardziej niezawodne ...
Greg Price,
3

Prawidłowym sposobem jest użycie tego, git log -L :function:path/to/fileco wyjaśniono w odpowiedzi eckes .

Ale dodatkowo, jeśli twoja funkcja jest bardzo długa, możesz chcieć zobaczyć tylko zmiany, które wprowadziły różne zatwierdzenia, a nie całe linie funkcji, w tym niezmodyfikowane, dla każdego zatwierdzenia, które może dotyczyć tylko jednej z tych linii. Jak normalny diff.

Zwykle git logmożna wyświetlać różnice -p, ale to nie działa -L. Musisz więc grep git log -Lpokazać tylko zaangażowane linie i nagłówki zatwierdzeń / plików, aby je kontekstualizować. Sztuczka polega na tym, aby dopasować tylko kolorowe linie terminala, dodać --colorprzełącznik, z wyrażeniem regularnym. Wreszcie:

git log -L :function:path/to/file --color | grep --color=never -E -e "^(^[\[[0-9;]*[a-zA-Z])+" -3

Zauważ, że ^[powinno to być rzeczywiste, dosłowne ^[. Możesz je wpisać naciskając ^ V ^ [w bash, czyli Ctrl+ V, Ctrl+ [. Odniesienie tutaj .

Również ostatni -3przełącznik pozwala wydrukować 3 wiersze kontekstu wyjściowego, przed i po każdej dopasowanej linii. Możesz chcieć dostosować go do swoich potrzeb.

Hazzard17
źródło
2

git blame pokazuje, kto ostatnio zmienił każdą linię pliku; możesz określić wiersze do zbadania, aby uniknąć pobierania historii wierszy poza twoją funkcję.

Będzie
źródło
4
z git gui blametobą możesz nawigować po starszych wersjach.
Vi.
0
  1. Pokaż historię funkcji za pomocą, git log -L :<funcname>:<file>jak pokazano w odpowiedzi eckes i git doc

    Jeśli nic nie pokazuje, zobacz Definiowanie niestandardowego nagłówka porcji, aby dodać coś podobnego *.java diff=javado .gitattributes pliku, aby obsługiwać twój język.

  2. Pokaż historię funkcji między zatwierdzeniami z git log commit1..commit2 -L :functionName:filePath

  3. Pokaż historię przeciążonych funkcji (może być wiele funkcji o tej samej nazwie, ale z różnymi parametrami) za pomocą git log -L :sum\(double:filepath

Kris Roofe
źródło