Comment trouver le commit Git qui a introduit une chaîne dans n'importe quelle branche?

396

Je veux pouvoir trouver une certaine chaîne qui a été introduite dans n'importe quel commit dans n'importe quelle branche, comment faire? J'ai trouvé quelque chose (que j'ai modifié pour Win32), mais git whatchangedne semble pas regarder dans les différentes branches (ignorez le morceau py3k, c'est juste un correctif de flux de ligne msys / win)

git whatchanged -- <file> | \
grep "^commit " | \
python -c "exec(\"import sys,msvcrt,os\nmsvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)\nfor l in sys.stdin: print(l.split()[1])\")" | \
xargs -i% git show origin % -- <file>

Peu importe que votre solution soit lente.

Jonas Byström
la source

Réponses:

685

Tu peux faire:

git log -S <whatever> --source --all

Pour trouver toutes les validations qui ont ajouté ou supprimé la chaîne fixe whatever . Le --allparamètre signifie commencer à partir de chaque branche et --sourcesignifie montrer laquelle de ces branches a conduit à trouver ce commit. Il est souvent utile d'ajouter -ppour montrer les correctifs que chacun de ces commits introduirait également.

Les versions de git depuis 1.7.4 ont également une -Goption similaire , qui prend une expression régulière . Cela a en fait une sémantique différente (et plutôt plus évidente), expliquée dans ce billet de blog de Junio ​​Hamano .

Comme le souligne thameera dans les commentaires, vous devez mettre des guillemets autour du terme de recherche s'il contient des espaces ou d'autres caractères spéciaux, par exemple:

git log -S 'hello world' --source --all
git log -S "dude, where's my car?" --source --all

Voici un exemple d'utilisation -Gpour rechercher des occurrences de function foo() {:

git log -G "^(\s)*function foo[(][)](\s)*{$" --source --all
Mark Longair
la source
19
+1 pour l'excellence. Pointer -S est une chose, mieux expliquer les choses. Aussi, je préfère utiliser --decorate pour voir quelles branches choses viennent de
sehe
7
@sehe: Merci pour votre gentil commentaire. Je suppose que cela vaut la peine de noter que --decoraten'ajoute que le nom de la branche au commit à la pointe de chaque branche. En pratique, je n'utilise pas vraiment --sourceou --decorate, et j'utilise plutôt git branch -a --contains <commit-hash>pour trouver quelles branches contiennent le commit qui m'intéresse.
Mark Longair
3
ajoutez -p pour voir également le diff en ligne, FWIW
rogerdpack
1
@MarkLongair, il n'affiche pas les modifications apportées lors de la fusion. Une suggestion pour les montrer aussi?
Pahlevi Fikri Auliya
2
Pour moi, cela ne fonctionne que si je supprime l'espace entre le -S et le terme de recherche, c'est-à-dire git log -S"dude, where's my car?" --source --all. @ribamar a également écrit cela dans une réponse ci-dessous, mais cela pourrait facilement être ignoré à côté de cette première réponse.
bug313
69

--reverse est également utile car vous voulez le premier commit qui a fait le changement:

git log --all -p --reverse --source -S 'needle'

De cette façon, les validations plus anciennes apparaîtront en premier.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
la source
20

La réponse de Mark Longair est excellente, mais j'ai trouvé que cette version plus simple fonctionnait pour moi.

git log -S whatever
Steven Penny
la source
24
Juste pour clarifier, cela fonctionne bien si le commit que vous recherchez se trouve HEAD, mais cette question particulière posait spécifiquement sur la recherche dans toutes les branches d'un référentiel.
Mark Longair
18

Déconner avec les mêmes réponses:

$ git config --global alias.find '!git log --color -p -S '
  • ! est nécessaire car autrement, git ne passe pas correctement l'argument à -S. Voir cette réponse
  • --color et -p aident à montrer exactement "ce qui a changé"

Maintenant vous pouvez faire

$ git find <whatever>

ou

$ git find <whatever> --all
$ git find <whatever> master develop
albfan
la source
6
git log -S"string_to_search" # options like --source --reverse --all etc

Faites attention à ne pas utiliser d'espaces entre S et "string_to_search". Dans certaines configurations (git 1.7.1), vous obtiendrez une erreur comme:

fatal: ambiguous argument 'string_to_search': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions
ribamar
la source
2

Bien que cela ne réponde pas directement à votre question, je pense que cela pourrait être une bonne solution pour vous à l'avenir. J'ai vu une partie de mon code, ce qui était mauvais. Je ne savais pas qui l'avait écrit ni quand. Je pouvais voir toutes les modifications du fichier, mais il était clair que le code avait été déplacé d'un autre fichier vers celui-ci. Je voulais savoir qui l'a ajouté en premier lieu.

Pour ce faire, j'ai utilisé Git bisect , qui m'a rapidement permis de trouver le pécheur.

J'ai couru git bisect startet puis git bisect bad, parce que la révision vérifiée avait le problème. Comme je ne savais pas quand le problème est survenu, je Ciblée la première commettras pour le « bon », git bisect good <initial sha>.

Ensuite, j'ai continué à chercher dans le référentiel le mauvais code. Quand je l' ai trouvé, je courais git bisect bad, et quand il était pas là: git bisect good.

En ~ 11 étapes, j'avais couvert environ 1000 commits et trouvé le commit exact, où le problème a été introduit. Assez bien.

Eldamir
la source
2

Je ne sais pas pourquoi la réponse acceptée ne fonctionne pas dans mon environnement, enfin je lance la commande ci-dessous pour obtenir ce dont j'ai besoin

git log --pretty=format:"%h - %an, %ar : %s"|grep "STRING"
BMW
la source