Comment «blâmer» une ligne supprimée?

506

git blameest idéal pour les lignes modifiées et ajoutées, mais comment savoir quand une ligne qui existait dans un commit précédent spécifique a finalement été supprimée. Je pense bisect, mais j'espérais quelque chose de plus pratique.

(Avant de demander: dans ce cas, je viens de faire un git log -pet j'ai cherché la ligne de code et (a) un idiot venait de supprimer la ligne vitale dans le commit précédent et (b) j'étais cet idiot.)

Malvolio
la source
4
Il y a un suivi avec une réponse clarifiant qui git log -S<string> /path/to/fileveut un -cou -ccaussi pour montrer les suppressions pendant la fusion (conflits)
cfi
3
Ce devrait être -cet --cc. @Steen: Correct, merci de l'avoir signalé! Surveillance stupide. J'aimerais pouvoir modifier le commentaire. Ajouter un nouveau, puis supprimer le mien, puis supprimer le vôtre est trop lourd, je suppose :)
cfi
4
Je souhaiterais git blameavoir une option pour afficher les lignes supprimées (avec peut-être un texte barré ou rouge) avec la révision dans laquelle elles ont été supprimées.
Craig McQueen
Serait-ce difficile à écrire? Je ne connais pas grand chose aux internes de Git.
Malvolio

Réponses:

636

Si vous connaissez le contenu de la ligne, c'est un cas d'utilisation idéal pour:

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

qui montre que vous vous engagez à introduire ou supprimer une instance de cette chaîne. Il y a aussi le -G<regex>qui fait la même chose avec les expressions régulières! Voir man git-loget rechercher les options -Get -S, ou pioche (le nom convivial de ces fonctionnalités) pour plus d'informations.

L' -Soption est également mentionnée dans l'en-tête de la git-blamepage de manuel, dans la section description, où elle donne un exemple d'utilisation git log -S....

Cascabel
la source
Brillant ... juste ce dont j'avais besoin dans ce travail de portage sur
lequel
37
Après avoir utilisé Git pendant plus d'un an, cela m'étonne toujours de voir que Git a toujours une commande / option quelque part pour répondre à presque tous les scénarios d'utilisation que j'ai. Merci d'avoir partagé celui-ci, c'est exactement ce dont j'ai besoin maintenant!
Pascal Bourque
22
Cette méthode a fonctionné pour moi auparavant, mais je viens de voir un cas où il n'a pas trouvé le commit où la ligne a été supprimée. Il s'est avéré que la ligne en question a été supprimée dans un commit de fusion - cela expliquerait-il l'échec? (La git blame --reverseméthode l'a trouvé cependant.)
antinome
9
@antinome Pour afficher les validations de la fusion, utilisez en plus l' -coption.
2014
2
J'ai fait un ctrl + f sur "-s" sur la page de manuel et je n'ai rien trouvé. Où sur la page le voyez-vous ?? J'utilise git 1.8.5.2
temporary_user_name
135

Je pense que tu veux vraiment

git blame --reverse START..END filename

Depuis la page de manuel :

Parcourez l'histoire au lieu de reculer. Au lieu d'afficher la révision dans laquelle une ligne est apparue, cela montre la dernière révision dans laquelle une ligne a existé. Cela nécessite une série de révisions comme START..END où le chemin du blâme existe dans START.

Avec git blame reverse, vous pouvez trouver le dernier commit dans lequel la ligne est apparue. Vous devez toujours obtenir le commit qui vient après.

Vous pouvez utiliser la commande suivante pour afficher un journal git inversé. Le premier commit affiché sera la dernière fois que cette ligne apparaîtra, et le prochain commit sera quand il sera modifié ou supprimé.

git log --reverse --ancestry-path COMMIT^..master
Chronial
la source
14
S'il y a plusieurs fusions de la branche où la ligne a été ajoutée dans la branche où la ligne est manquante (ou tout autre cas où il y a plusieurs chemins dans la lignée de START à END), git blame --reverseaffichera la révision avant la fusion qui était chronologiquement enfin, pas la révision avant la fusion initiale où la décision a été prise de ne pas prendre la ligne. Existe-t-il un moyen de trouver la première révision là où la ligne a cessé d'exister plutôt que la plus récente?
rakslice
2
@rakslice, pour cela vous pouvez utiliser le blâme --reverse --first-parent, c'est un peu mieux.
max630
17

Pour compléter la réponse de Cascabel :

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

J'ai eu le même problème que celui mentionné ici, mais il s'est avéré que la ligne était manquante, car une validation de fusion d'une branche a été annulée puis fusionnée à nouveau, supprimant effectivement la ligne en question. Le --full-historydrapeau empêche de sauter ces commits.

estani
la source
9

git blame --reverse peut vous rapprocher de l'endroit où la ligne est supprimée. Mais cela ne pointe pas vers la révision où la ligne est supprimée. Il pointe vers la dernière révision où la ligne était présente . Ensuite, si la révision suivante est un commit simple, vous avez de la chance et vous avez obtenu la révision de suppression. OTOH, si la révision suivante est un commit de fusion , alors les choses peuvent devenir un peu folles.

Dans le cadre de l'effort de création de difflame, j'ai abordé ce problème même si vous avez déjà installé Python sur votre boîte et que vous êtes prêt à l'essayer, n'attendez plus et faites-moi savoir comment cela se passe.

https://github.com/eantoranz/difflame

eftshift0
la source
0

Pour les modifications masquées dans les validations de fusion

Les validations de fusion ont automatiquement leurs modifications masquées dans la sortie du journal Git. La pioche et le blâme inversé n'ont pas trouvé le changement. Donc, la ligne que je voulais avait été ajoutée puis supprimée et je voulais trouver la fusion qui la supprimait. L' git log -p -- path/filehistorique du fichier n'a montré que son ajout. Voici le meilleur moyen que j'ai trouvé pour le trouver:

git log -p -U9999 -- path/file

Recherchez la modification, puis recherchez en arrière "^ commit" - le premier "^ commit" est le commit où le fichier avait cette ligne en dernier. Le deuxième "^ commit" est après sa disparition. Le deuxième commit peut être celui qui l'a supprimé. Le -U9999est destiné à afficher l'intégralité du contenu du fichier (après chaque modification du fichier), en supposant que vos fichiers sont tous composés de 9 999 lignes maximum.

Recherche toutes les fusions associées via la force brute (diff chaque validation de fusion possible avec son premier parent, exécuté contre des tonnes de validations)

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

(J'ai essayé de restreindre davantage le filtre de révision, mais j'ai rencontré des problèmes et je ne le recommande pas. Les modifications d'ajout / suppression que je cherchais étaient sur différentes branches qui ont été fusionnées à différents moments et A ... B n'incluait pas lorsque les modifications ont été fusionnées dans la ligne principale.)

Afficher un arbre Git avec ces deux validations (et une grande partie de l'historique Git complexe supprimé):

git log --graph --oneline A B ^$(git merge-base A B) (A est le premier commit ci-dessus, B est le deuxième commit ci-dessus)

Afficher l'historique de A et l'histoire de B moins l'historique de A et de B.

Version alternative (semble montrer le chemin de façon plus linéaire plutôt que l'arbre d'historique Git normal - cependant je préfère l'arbre d'historique git normal):

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

Trois, pas deux points - trois points signifie «r1 r2 - pas $ (git merge-base --all r1 r2). côté), mais pas des deux. " - source: "man gitrevisions"

Curtis Yallop
la source