Czy pobrać dziennik zatwierdzeń dla określonej linii w pliku?

502

Czy jest jakiś sposób, aby git dostarczył ci dziennik zatwierdzeń dla samych zatwierdzeń, które dotknęły określonej linii w pliku?

Podobnie jak git blame, ale git blamepokaże OSTATNIE zatwierdzenie, które dotknęło określonego wiersza.

Naprawdę chciałbym uzyskać podobny dziennik, nie listę zatwierdzeń do dowolnego miejsca w pliku, ale tylko zatwierdzenia, które dotknęły określonej linii.

jrochkind
źródło
2
Zobacz także: Git winić - wcześniejsze zobowiązania
joeytwiddle

Odpowiedzi:

631

Zobacz także Git: odkryj, które zmiany dotknęły szeregu linii .


Ponieważ Git 1.8.4 , git logma -Lby zobaczyć ewolucję różnych liniach.

Załóżmy na przykład, że patrzysz na git blamewynik. Tutaj -L 150,+11oznacza „patrzeć tylko na liniach 150 do 150 + 11”:

$ git blame -L 150,+11 -- git-web--browse.sh
a180055a git-web--browse.sh (Giuseppe Bilotta 2010-12-03 17:47:36 +0100 150)            die "The browser $browser is not
a180055a git-web--browse.sh (Giuseppe Bilotta 2010-12-03 17:47:36 +0100 151)    fi
5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 152) fi
5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 153) 
5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 154) case "$browser" in
81f42f11 git-web--browse.sh (Giuseppe Bilotta 2010-12-03 17:47:38 +0100 155) firefox|iceweasel|seamonkey|iceape)
5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 156)    # Check version because firefox < 2.0 do
5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 157)    vers=$(expr "$($browser_path -version)" 
5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 158)    NEWTAB='-new-tab'
5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 159)    test "$vers" -lt 2 && NEWTAB=''
a0685a4f git-web--browse.sh (Dmitry Potapov   2008-02-09 23:22:22 -0800 160)    "$browser_path" $NEWTAB "$@" &

I chcesz poznać historię tego, co jest teraz linią 155.

Następnie użyj git log. Tutaj -L 155,155:git-web--browse.shoznacza „śledź ewolucję linii 155 do 155 w pliku o nazwie git-web--browse.sh”.

$ git log --pretty=short -u -L 155,155:git-web--browse.sh
commit 81f42f11496b9117273939c98d270af273c8a463
Author: Giuseppe Bilotta <[email protected]>

    web--browse: support opera, seamonkey and elinks

diff --git a/git-web--browse.sh b/git-web--browse.sh
--- a/git-web--browse.sh
+++ b/git-web--browse.sh
@@ -143,1 +143,1 @@
-firefox|iceweasel)
+firefox|iceweasel|seamonkey|iceape)

commit a180055a47c6793eaaba6289f623cff32644215b
Author: Giuseppe Bilotta <[email protected]>

    web--browse: coding style

diff --git a/git-web--browse.sh b/git-web--browse.sh
--- a/git-web--browse.sh
+++ b/git-web--browse.sh
@@ -142,1 +142,1 @@
-    firefox|iceweasel)
+firefox|iceweasel)

commit 5884f1fe96b33d9666a78e660042b1e3e5f9f4d9
Author: Christian Couder <[email protected]>

    Rename 'git-help--browse.sh' to 'git-web--browse.sh'.

diff --git a/git-web--browse.sh b/git-web--browse.sh
--- /dev/null
+++ b/git-web--browse.sh
@@ -0,0 +127,1 @@
+    firefox|iceweasel)
Matt McClure
źródło
2
„git log --topo-order --graph -u -L 155,155: git-web--browse.sh” - to spowodowało błąd krytyczny: „niepoprawna nazwa obiektu 155, 155”. Wersja Git: 1.8.3.2. Jakieś sugestie?
BairDev,
12
Uaktualnij do wersji Git 1.8.4 lub nowszej.
Matt McClure,
4
co jeśli chcę poznać „historię linii 155 w commitA” (zamiast linii 155 w HEAD). Czy mogę po prostu użyć git log commitA-hash -L 155,155:file-name?
Ida
@Flimm, nie mam silnych preferencji.
Matt McClure,
działa to dobrze, z wyjątkiem sytuacji, gdy plik został przeniesiony / zmieniono jego nazwę ... wydaje się, że - follow nie lubi być łączony z argumentami zakresu linii.
Mike Ellery
66

Możesz uzyskać zestaw zatwierdzeń za pomocą kilofu.

git log -S'the line from your file' -- path/to/your/file.txt

To da ci wszystkie zmiany, które wpłynęły na tekst w tym pliku. Jeśli nazwa pliku została w pewnym momencie zmieniona, możesz dodać opcję --follow-parent.

Jeśli chcesz sprawdzić zatwierdzenia przy każdej z tych edycji, możesz przesłać wyniki do git show:

git log ... | xargs -n 1 git show
Adam Dymitruk
źródło
5
Nie jestem pewien, czy widzę, jak to pomaga. Jeśli to wpłynęło na tekst, linia nie jest już taka sama, więc kilof wyświetli tylko ostatnią zmianę. Musiałbyś wtedy zrobić git log -S'the previous version of the line'i tak dalej, dokładnie tak, jak byś to zrobił git blame -L. I będzie o wiele wolniejszy niż git blame, ponieważ musi szukać tekstu wszędzie, nie tylko w danym miejscu.
Cascabel,
Możesz użyć wyrażenia regularnego, aby uczynić go bardziej akceptowalnym. Obecnie nie ma możliwości „nawigowania po łatkach” w przeszłość bez skomplikowanych skryptów. Mam nadzieję, że gitk dostanie tę funkcjonalność w widoku łat w przyszłości.
Adam Dymitruk,
3
dlaczego nazywa się to kilofem ? :)
Ciprian Tomoiagă
43

Spróbuj użyć poniższego polecenia zaimplementowanego w Git 1.8.4.

git log -u -L <upperLimit>,<lowerLimit>:<path_to_filename>

Więc w twoim przypadku upperLimiti lowerLimitjest dotkniętyline_number

Więcej informacji - https://www.techpurohit.com/list-some-useful-git-commands

jitendrapurohit
źródło
2
Myślę, że --pretty=shortjest ignorowany podczas używania -L. Proszę poprawić
Vic Seedoubleyew
14

Niezwykle łatwym sposobem na to jest użycie vim-fugitive . Po prostu otwórz plik w vimie, wybierz linie, których chcesz użyć V, a następnie wpisz

:Glog

Teraz możesz użyć :cnexti, :cprevaby zobaczyć wszystkie wersje pliku, w którym wiersz został zmodyfikowany. W dowolnym momencie wejdź, :Gblameaby zobaczyć sha, autora i datę.

Cory Klein
źródło
13

Uproszczenie odpowiedzi @ matt -

git blame -L14,15 -- <file_path>

Tutaj otrzymasz winę za linie 14 to 15.

Ponieważ -Lopcja oczekuje się Rangejako parametru, nie możemy uzyskać Blamedla pojedynczej linii przy użyciu -Lopcji` .

Odniesienie

Zamiana
źródło
10

Nie wierzę, że jest w tym coś wbudowanego. Utrudnia to fakt, że pojedyncza linia zmienia się kilka razy bez znacznej zmiany reszty pliku, więc często dochodzi do zmiany numerów linii.

Jeśli masz szczęście, że linia zawsze ma jakąś cechę identyfikującą, np. Przypisanie do zmiennej, której nazwa nigdy się nie zmienia, możesz użyć wyrażenia regularnego dla git blame -L. Na przykład:

git blame -L '/variable_name *= */',+1

Ale to tylko pierwszy dopasowanie dla tego wyrażenia regularnego, więc jeśli nie masz dobrego sposobu dopasowania linii, nie jest to zbyt pomocne.

Przypuszczam, że możesz coś zhakować. Nie mam teraz czasu na pisanie kodu, ale ... coś w tym stylu. Uruchom git blame -n -L $n,$n $file. Pierwsze pole jest dotknięciem poprzedniego zatwierdzenia, a drugie pole to numer wiersza w tym zatwierdzeniu, ponieważ mógł on ulec zmianie. Chwyć je i uruchom git blame -n $n,$n $commit^ $file, tzn. To samo, zaczynając od zatwierdzenia przed ostatnią zmianą pliku.

(Zauważ, że to Cię nie powiedzie, jeśli ostatni zatwierdzenie, które zmieniło linię, było zatwierdzeniem scalania. Podstawowym sposobem może się to stać, jeśli zmieniono wiersz w ramach rozwiązywania konfliktu scalenia.)

Edycja: Spotkałem się dzisiaj z postem na liście mailowej od marca 2011 roku, który wspomina o tym tigi git guima funkcję, która pomoże ci to zrobić. Wygląda na to, że ta funkcja została rozważona, ale nie ukończona, dla samego gita.

Cascabel
źródło
6

Wywoła to git blamedla każdej znaczącej rewizji, aby pokazać linię $LINEpliku $FILE:

git log --format=format:%H $FILE | xargs -L 1 git blame $FILE -L $LINE,$LINE

Jak zwykle, wina pokazuje numer wersji na początku każdej linii. Możesz dołączyć

| sort | uniq -c

aby uzyskać zagregowane wyniki, coś w rodzaju listy zatwierdzeń, które zmieniły ten wiersz. (Niezupełnie, jeśli tylko kod został przeniesiony, może to pokazywać dwa razy ten sam identyfikator zatwierdzenia dla różnych treści wiersza. Aby uzyskać bardziej szczegółową analizę, należy wykonać opóźnione porównanie git blamewyników dla sąsiednich zatwierdzeń. Ktoś? )

krlmlr
źródło
Ponownie myślę, że to nie działa, ponieważ nie śledzi poprzedniej lokalizacji tej linii. Więc jeśli linia została dodana 2 zatwierdzenia temu,
patrzyłbyś
6

Oto rozwiązanie, które definiuje alias git, dzięki czemu będziesz mógł go używać w ten sposób:

git rblame -M -n -L '/REGEX/,+1' FILE

Przykład wyjściowy:

00000000 18 (Not Committed Yet 2013-08-19 13:04:52 +0000 728) fooREGEXbar
15227b97 18 (User1 2013-07-11 18:51:26 +0000 728) fooREGEX
1748695d 23 (User2 2013-03-19 21:09:09 +0000 741) REGEXbar

Możesz zdefiniować alias w .gitconfig lub po prostu uruchomić następującą komendę

git config alias.rblame !sh -c 'while line=$(git blame "$@" $commit 2>/dev/null); do commit=${line:0:8}^; [ 00000000^ == $commit ] && commit=$(git rev-parse HEAD); echo $line; done' dumb_param

To jest brzydka jednowarstwowa, więc tutaj jest zaciemniona równoważna funkcja bash:

git-rblame () {
    local commit line
    while line=$(git blame "$@" $commit 2>/dev/null); do
        commit="${line:0:8}^"
        if [ "00000000^" == "$commit" ]; then
            commit=$(git rev-parse HEAD)
        fi
        echo $line
    done
}

Rozwiązanie kilof ( git log --pickaxe-regex -S'REGEX ' ) daje tylko dodawanie / usuwanie wierszy, a nie inne zmiany wiersza zawierającego wyrażenie regularne.

Ograniczeniem tego rozwiązania jest ta wina zwraca tylko 1. dopasowanie REGEX, więc jeśli istnieje wiele dopasowań, rekurencja może „przeskoczyć”, by podążać inną linią. Upewnij się, że sprawdziłeś pełną historię, aby wykryć te „skoki”, a następnie napraw REGEX, aby zignorować linie pasożytów.

Na koniec, oto alternatywna wersja, która uruchamia git show przy każdym zatwierdzeniu, aby uzyskać pełną różnicę:

git config alias.rblameshow !sh -c 'while line=$(git blame "$@" $commit 2>/dev/null); do commit=${line:0:8}^; [ 00000000^ == $commit ] && commit=$(git rev-parse HEAD); git show $commit; done' dumb_param
Lucas Cimon
źródło
2

Możesz mieszać git blamei git logpolecenia, aby pobrać podsumowanie każdego zatwierdzenia w poleceniu git blame i dołączyć je. Coś takiego jak poniższy skrypt bash + awk. Dołącza podsumowanie zatwierdzenia jako wbudowany komentarz kodu.

git blame FILE_NAME | awk -F" " \
'{
   commit = substr($0, 0, 8);
   if (!a[commit]) {
     query = "git log --oneline -n 1 " commit " --";
     (query | getline a[commit]);
   }
   print $0 "  // " substr(a[commit], 9);
 }'

W jednej linii:

git blame FILE_NAME | awk -F" " '{ commit = substr($0, 0, 8); if (!a[commit]) { query = "git log --oneline -n 1 " commit " --"; (query | getline a[commit]); } print $0 "  // " substr(a[commit], 9); }'
cserpell
źródło
2

W moim przypadku numer linii bardzo się zmienił w czasie. Byłem także na git 1.8.3, który nie obsługuje wyrażenia regularnego w „git blame -L”. (RHEL7 nadal ma 1.8.3)

myfile=haproxy.cfg
git rev-list HEAD -- $myfile | while read i
do
    git diff -U0 ${i}^ $i $myfile | sed "s/^/$i /"
done | grep "<sometext>"

Oneliner:

myfile=<myfile> ; git rev-list HEAD -- $myfile | while read i; do     git diff -U0 ${i}^ $i $myfile | sed "s/^/$i /"; done | grep "<sometext>"

Można to oczywiście przekształcić w skrypt lub funkcję.

Sastorsl
źródło