Come posso "dare la colpa" a una riga eliminata?


506

git blameè ottimo per le righe modificate e aggiunte, ma come posso trovare quando una riga esistente in uno specifico commit precedente è stata infine eliminata. Sto pensando bisect, ma speravo in qualcosa di più maneggevole.

(Prima di chiedere: in questo caso, ho appena fatto una git log -pricerca per la riga di codice e (a) qualche idiota aveva appena eliminato la riga vitale nel commit precedente e (b) ero quell'idiota.)


4
C'è un seguito con una risposta che chiarisce che git log -S<string> /path/to/filevuole un -co -ccanche per mostrare le rimozioni durante l'unione (conflitti)
cfi

3
Dovrebbe essere -ce --cc. @Steen: Esatto, grazie per averlo sottolineato! Sorveglianza stupida. Vorrei poter modificare il commento. Aggiungerne uno nuovo, quindi eliminare il mio, quindi eliminare il tuo è troppo ingombrante, immagino :)
cfi,

4
Vorrei git blameavere un'opzione per mostrare le righe cancellate (con forse barrato o testo rosso) con la revisione in cui sono state eliminate.
Craig McQueen,

Sarebbe difficile da scrivere? Non so molto degli interni di Git.
Malvolio,

Risposte:


636

Se conosci il contenuto della linea, questo è un caso d'uso ideale per:

git log -S <string> path/to/file

che mostra i commit che introducono o rimuovono un'istanza di quella stringa. C'è anche quello -G<regex>che fa la stessa cosa con le espressioni regolari! Vedere man git-loge cercare il -Ge-S opzioni , oppure piccone (il nome descrittivo di queste funzionalità) per ulteriori informazioni.

L' -Sopzione è effettivamente menzionata anche nell'intestazione della git-blamemanpage, nella sezione descrizione, dove fornisce un esempio usando git log -S....


Fantastico ... proprio quello di cui avevo bisogno in questo lavoro di porting Sto lavorando al +1
jkp

37
Dopo aver usato Git per oltre 1 anno, mi stupisce ancora vedere che Git abbia sempre un comando / un'opzione da qualche parte per affrontare quasi tutti gli scenari di utilizzo che ho. Grazie per aver condiviso questo, è esattamente quello di cui ho bisogno ora!
Pascal Bourque,

22
Questo metodo ha funzionato per me prima, ma proprio ora ho visto un caso in cui non è stato trovato il commit in cui la riga è stata eliminata. Si è scoperto che la riga in questione è stata eliminata in un commit di merge - questo spiegherebbe l'errore? (Il git blame --reversemetodo ha trovato però.)
antinome

9
@antinome Per mostrare i commit dall'unione, utilizzare l' -copzione in aggiunta.
yunzen,

2
Ho fatto un ctrl + f su "-s" nella manpage e non ho trovato nulla. Dove sulla pagina lo vedi ?? Sto usando git 1.8.5.2
temporary_user_name

135

Penso che quello che vuoi davvero sia

git blame --reverse START..END filename

Dalla manpage :

Cammina la storia in avanti anziché indietro. Invece di mostrare la revisione in cui è apparsa una linea, mostra l'ultima revisione in cui esiste una linea. Ciò richiede una serie di revisioni come START..END in cui esiste il percorso da incolpare in START.

Con git blame reverse , puoi trovare l'ultimo commit in cui appare la linea. Devi ancora ottenere il commit che viene dopo.

È possibile utilizzare il comando seguente per mostrare un registro git invertito. Il primo commit visualizzato sarà l'ultima volta che appare la riga e il commit successivo sarà quando viene modificato o rimosso.

git log --reverse --ancestry-path COMMIT^..master

14
Se ci sono più fusioni dal ramo in cui la linea è stata aggiunta al ramo in cui manca la linea (o qualsiasi altro caso in cui ci sono più percorsi nel lignaggio da START a END), git blame --reversemostrerà la revisione prima dell'unione che era cronologicamente infine, non la revisione prima della fusione iniziale in cui è stata presa la decisione di non prendere la linea. C'è un modo per trovare la prima revisione in cui la linea ha smesso di esistere piuttosto che la più recente?
rakslice,

2
@rakslice, per questo puoi usare la colpa --reverse - first-parent, è leggermente meglio.
max630,

17

Solo per completare la risposta di Cascabel :

git log --full-history -S <string> path/to/file

Ho avuto lo stesso problema menzionato qui, ma si è scoperto che la linea mancava, perché un commit di unione da un ramo è stato ripristinato e poi si è fuso di nuovo in esso, rimuovendo efficacemente la linea in questione. La --full-historybandiera impedisce di saltare quei commit.


9

git blame - il ritorno può farti avvicinare al punto in cui la linea viene eliminata. Ma in realtà non punta alla revisione in cui la riga viene eliminata. Indica l' ultima revisione in cui era presente la linea . Quindi se la seguente revisione è un semplice commit, sei fortunato e hai ottenuto la cancellazione della revisione. OTOH, se la seguente revisione è un commit di unione , allora le cose possono diventare un po 'selvagge.

Come parte dello sforzo di creare difflame ho affrontato proprio questo problema, quindi se hai già installato Python sulla tua scatola e sei disposto a provarlo, allora non aspettare oltre e fammi sapere come va.

https://github.com/eantoranz/difflame


0

Per le modifiche nascoste in Merge commit

Unisci commit automaticamente le loro modifiche sono nascoste dall'output del registro Git. Sia il piccone che la colpa inversa non hanno trovato il cambiamento. Quindi la linea che volevo era stata aggiunta e successivamente rimossa e volevo trovare l'unione che la rimuoveva. La git log -p -- path/filecronologia dei file ha mostrato solo che è stata aggiunta. Ecco il modo migliore che ho trovato per trovarlo:

git log -p -U9999 -- path/file

Cerca la modifica, quindi cerca all'indietro "^ commit" - il primo "^ commit" è il commit in cui il file aveva l'ultima riga. Il secondo "^ commit" è dopo che è scomparso. Il secondo commit potrebbe essere quello che lo ha rimosso. Il-U9999 obiettivo è mostrare l'intero contenuto del file (dopo ogni modifica del file), supponendo che i file abbiano tutte un massimo di 9999 righe.

Trova eventuali fusioni correlate tramite forza bruta (diff ogni possibile unione di commit con il suo primo genitore, contro tonnellate di commit)

git log --merges --pretty=format:"git diff %h^...%h | grep target_text" HEAD ^$(git merge-base A B) | sh -v 2>&1 | less

(Ho provato a limitare maggiormente il filtro di revisione, ma ho riscontrato problemi e non lo consiglio. Le modifiche di aggiunta / rimozione che stavo cercando erano su rami diversi che sono stati uniti in momenti diversi e A ... B non includeva quando le modifiche sono state effettivamente integrate nella linea principale.)

Mostra un albero Git con questi due commit (e molta della complessa storia di Git rimossa):

git log --graph --oneline A B ^$(git merge-base A B) (A è il primo commit sopra, B è il secondo commit sopra)

Mostra la storia di A e la storia di B meno la storia di A e B.

Versione alternativa (sembra mostrare il percorso in modo più lineare rispetto al normale albero della cronologia di Git - tuttavia preferisco il normale albero della cronologia di Git):

git log --graph --oneline A...B

Tre, non due punti - tre punti significa "r1 r2 --not $ (git merge-base --all r1 r2). È l'insieme di commit che sono raggiungibili da uno di r1 (lato sinistro) o r2 (destro lato), ma non da entrambi. " - fonte: "man gitrevisions"

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.