Recupera il log di commit per una riga specifica in un file?

C’è un modo per ottenere git per darti un log di commit solo per i commit che hanno toccato una particolare riga in un file?

Come git blame , ma git blame ti mostrerà l’ULTIMO commit che ha toccato una particolare linea.

Mi piacerebbe davvero avere un registro simile, non l’elenco di commit in qualsiasi parte del file, ma solo i commit che hanno toccato una determinata riga.

Vedi anche Git: scopri quale commit ha mai toccato una serie di linee .


Da Git 1.8.4 , git log ha -L per visualizzare l’evoluzione di un intervallo di linee.

Ad esempio, supponiamo di guardare l’output di git blame . Qui -L 150,+11 significa “guarda solo le righe da 150 a 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 "$@" & 

E tu vuoi conoscere la storia di quella che ora è la linea 155.

Quindi, usa git log . Qui, -L 155,155:git-web--browse.sh significa "traccia l'evoluzione delle righe da 155 a 155 nel file chiamato git-web--browse.sh ".

 $ git log --pretty=short -u -L 155,155:git-web--browse.sh commit 81f42f11496b9117273939c98d270af273c8a463 Author: Giuseppe Bilotta  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  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  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) 

Puoi ottenere una serie di commit usando pick-ax.

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

Questo ti darà tutti i commit che hanno influenzato quel testo in quel file. Se il file è stato rinominato a un certo punto, puoi aggiungere –follow-parent.

Se desideri esaminare i commit in ciascuna di queste modifiche, puoi redirect il risultato a git show:

 git log ... | xargs -n 1 git show 

Prova a usare il comando qui sotto implementato in Git 1.8.4.

 git log --pretty=short -u -L ,: 

Quindi, nel tuo caso upperLimit e lowerLimit è il numero di linea toccato

Un modo estremamente semplice per farlo è usare vim-fugitive . Basta aprire il file in vim, selezionare la / e linea / e che ti interessa usare V , quindi inserire

 :Glog 

Ora puoi usare :cnext e :cprev per vedere tutte le revisioni del file in cui quella riga è stata modificata. In qualsiasi momento, inserisci :Gblame per vedere le informazioni su sha, autore e data.

Non credo che ci sia qualcosa di built-in per questo. È complicato dal fatto che è raro che una singola riga cambi più volte senza che il resto del file cambi sostanzialmente, quindi tenderai a far cambiare molto i numeri di riga.

Se sei abbastanza fortunato che la linea ha sempre alcune caratteristiche identificative, ad esempio un assegnamento a una variabile il cui nome non è mai cambiato, potresti usare la scelta regex per git blame -L . Per esempio:

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

Ma questo trova solo la prima corrispondenza per quella regex, quindi se non hai un buon modo per far corrispondere la linea, non è troppo utile.

Potresti incidere qualcosa, suppongo. Non ho tempo di scrivere il codice solo ora, ma … qualcosa in questo senso. Esegui git blame -n -L $n,$n $file . Il primo campo è il commit precedente toccato e il secondo campo è il numero di riga in quel commit, poiché potrebbe essere cambiato. Prendili e avvia git blame -n $n,$n $commit^ $file , ovvero la stessa cosa che inizia dal commit prima dell’ultima modifica del file.

(Si noti che ciò non riuscirà se l’ultimo commit che ha modificato la riga è stato un commit di unione. Il modo principale potrebbe verificarsi se la riga è stata modificata come parte di una risoluzione di conflitto di tipo merge).

Modifica: Mi sono imbattuto in questo post di mailing list di oggi, nel marzo 2011, che menziona che tig e git gui hanno una funzione che ti aiuterà a farlo. Sembra che la funzione sia stata considerata, ma non completata, per git stesso.

Ecco una soluzione che definisce un alias git, quindi sarai in grado di usarlo in questo modo:

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

Esempio di uscita:

 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 

Puoi definire l’alias nel tuo .gitconfig o semplicemente eseguire il seguente comando

 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 

Questo è un brutto one-liner, quindi ecco una funzione bash equivalente de-offuscata:

 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 } 

La soluzione pickaxe ( git log –pickaxe-regex -S’REGEX ‘ ) fornirà solo aggiunte / cancellazioni di riga, non le altre alterazioni della riga contenente l’espressione regolare.

Una limitazione di questa soluzione è che git blame restituisce solo la prima corrispondenza di REGEX, quindi se esistono più corrispondenze la ricorsione può “saltare” per seguire un’altra linea. Assicurati di controllare l’intera cronologia di output per individuare quei “salti” e quindi correggere il tuo REGEX per ignorare le linee del parassita.

Infine, ecco una versione alternativa che esegue git show su ogni commit per ottenere la differenza completa:

 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 

Semplificazione della risposta di @ matt –

git blame -L14,15 --

Qui avrai una colpa per le righe da 14 to 15 .

Dato che l’opzione -L prevede che Range come parametro non possiamo ottenere una Blame per una singola riga usando l’opzione -L` .

Riferimento

Questo chiamerà git blame per ogni revisione significativa per mostrare la riga $LINE del file $FILE :

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

Come al solito, la colpa mostra il numero di revisione all’inizio di ogni riga. Puoi aggiungere

 | sort | uniq -c 

per ottenere risultati aggregati, qualcosa come una lista di commit che ha cambiato questa linea. (Non proprio, se il codice è stato spostato solo, questo potrebbe mostrare lo stesso ID di commit due volte per diversi contenuti della linea. Per un’analisi più dettagliata dovresti fare un confronto ritardato dei risultati della git blame per i commit adiacenti. chiunque?)

Nel mio caso il numero di riga è cambiato molto nel tempo. Ero anche su git 1.8.3 che non supporta regex in “git blame -L”. (RHEL7 ha ancora 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 "" 

Oneliner:

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

Questo può naturalmente essere trasformato in uno script o una funzione.

È ansible combinare i comandi git blame e git log per recuperare il riepilogo di ogni commit nel comando git blame e aggiungerli. Qualcosa come il seguente script bash + awk. Aggiunge il riepilogo del commit come commento di codice incorporato.

 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); }' 

In una riga:

 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); }'