Comment rechercher dans les branches Git un fichier ou un répertoire?

323

Dans Git, comment puis-je rechercher un fichier ou un répertoire par chemin dans plusieurs branches?

J'ai écrit quelque chose dans une branche, mais je ne me souviens pas laquelle. Maintenant, je dois le trouver.

Précision : je recherche un fichier que j'ai créé sur une de mes succursales. Je voudrais le trouver par chemin, et non par son contenu, car je ne me souviens pas du contenu.

Peeja
la source
Je ne suis pas clair sur la question. Ce fichier était-il dans une branche qui est maintenant supprimée? Le fichier a-t-il été supprimé?
Abizern

Réponses:

444

git log+ git branchle trouvera pour vous:

% git log --all -- somefile

commit 55d2069a092e07c56a6b4d321509ba7620664c63
Author: Dustin Sallings <[email protected]>
Date:   Tue Dec 16 14:16:22 2008 -0800

    added somefile


% git branch -a --contains 55d2069
  otherbranch

Prend également en charge le globbing:

% git log --all -- '**/my_file.png'

Les guillemets simples sont nécessaires (au moins si vous utilisez le shell Bash) afin que le shell passe le modèle glob à git inchangé, au lieu de le développer (comme avec Unix find).

Dustin
la source
2
Cela fonctionne si vous connaissez le chemin exact vers somefile: si vous avez besoin d'une recherche d'expression régulière sur le chemin / nom de fichier, par exemple, vous pouvez utiliser la réponse d'ididak.
ShreevatsaR
70
Prend également en charge la git log --all -- **/my_file.png
globalisation
3
Je l'ai utilisé pour un problème légèrement différent. J'essayais de trouver tous les fichiers * .sql que j'avais engagés dans une branche particulière. J'ai donc utilisé git log --grep='branch' --author='me' -- *.sql. A fonctionné comme un charme. git version 1.7.11.1
thepriebe
1
Notez que gitkprend également en charge le globbing. C'est une excellente réponse @webmat! Si vous voulez voir où il a été supprimé / créé / etc, vous pouvez l'utiliser git log --all --stat -- **/my_file.png, de cette façon, vous n'aurez pas à deviner si vous le retirez d'un commit qui l'a supprimé.
eacousineau
1
@webmat: J'ai pris la liberté d'ajouter vos informations sur la globalisation à la réponse. Merci de l'avoir mentionné!
sleske
65

git ls-tree pourrait aider. Pour rechercher dans toutes les branches existantes:

for branch in `git for-each-ref --format="%(refname)" refs/heads`; do
  echo $branch :; git ls-tree -r --name-only $branch | grep '<foo>'
done

L'avantage de ceci est que vous pouvez également rechercher des expressions régulières pour le nom de fichier.

ididak
la source
3
Quelques commentaires utiles, espérons-le: (a) Vous voudrez probablement ajouter "-r" à "git ls-tree" afin qu'il trouve le fichier même s'il se trouve dans un sous-répertoire. (b) Une alternative peut-être plus nette à la première ligne serait d'utiliser "git for-each ref", par exemple "pour branch in git for-each-ref --format="%(refname)" refs/heads; do" (c) "git ls-tree --name-only" rendra la sortie plus nette (d) il pourrait être utile de souligner dans votre réponse qu'il y a un avantage à cela par rapport à la solution de Dustin, à savoir que vous n'avez pas besoin de connaître exactement le nom de fichier - vous pouvez rechercher par expression régulière correspondant à n'importe quelle partie du chemin
Mark Longair
9
J'ai enveloppé cela dans un script pour que vous puissiez exécuter "gitfind.sh <regex>"; l'essentiel est gist.github.com/62d981890eccb48a99dc
Homme à tout
3
Notez que cela ne recherchera que les succursales locales . Pour rechercher des branches de suivi à distance, utilisez refs/remotesau lieu de refs/heads. Pour tout rechercher (succursales locales, succursales de suivi à distance et balises), utilisez simplement refs/.
sleske
19

Bien que la réponse d'ididak soit plutôt cool, et Handyman5 fournit un script pour l'utiliser, je l'ai trouvé un peu limité pour utiliser cette approche.

Parfois, vous devez rechercher quelque chose qui peut apparaître / disparaître avec le temps, alors pourquoi ne pas chercher contre tous les commits? En plus de cela, vous avez parfois besoin d'une réponse détaillée, et d'autres fois, ne validez que les correspondances. Voici deux versions de ces options. Mettez ces scripts sur votre chemin:

git-find-file

for branch in $(git rev-list --all)
do
  if (git ls-tree -r --name-only $branch | grep --quiet "$1")
  then
     echo $branch
  fi
done

git-find-file-verbose

for branch in $(git rev-list --all)
do
  git ls-tree -r --name-only $branch | grep "$1" | sed 's/^/'$branch': /'
done

Maintenant vous pouvez faire

$ git find-file <regex>
sha1
sha2

$ git find-file-verbose <regex>
sha1: path/to/<regex>/searched
sha1: path/to/another/<regex>/in/same/sha
sha2: path/to/other/<regex>/in/other/sha

Voyez qu'en utilisant getopt vous pouvez modifier ce script pour alterner la recherche de tous les commits, refs, refs / heads, été verbeux, etc.

$ git find-file <regex>
$ git find-file --verbose <regex>
$ git find-file --verbose --decorated --color <regex>

Consultez https://github.com/albfan/git-find-file pour une implémentation possible.

albfan
la source
Vouliez-vous dire "Maintenant vous pouvez faire git-find-file <regex>"? Je l'ai fait fonctionner, merci!
Elijah Lynn
1
Personnellement, je trouve beaucoup plus utile d'utiliser $ (git branch | cut -c 3-) au lieu de $ (git rev-list --all). Je ne me soucie pas de trouver un tas de commits, je me soucie des branches nommées.
Campadrenalin
J'ai également changé les scripts pour utiliser $ (git branch | cut -c 3-) car la boucle sur toutes les validations était beaucoup trop lente.
2015
Vu tout ce truc que je vaux vaut la peine de créer un repo pour ça. github.com/albfan/git-find-file . J'ai ouvert quelques problèmes, n'hésitez pas à en proposer plus ou à envoyer des RP pour cela.
albfan
1
@lumbric, l'installation du script sur le chemin fonctionne immédiatement, c'est une fonctionnalité git
albfan
10

Vous pouvez utiliser gitk --allet rechercher des commits "touchant les chemins" et le chemin d'accès qui vous intéresse.

Greg Hewgill
la source
3
Comme indiqué, utilisez gitk --all, puis dans Affichage | Nouvelle vue, activez Toutes les branches. Définissez ensuite vos critères de recherche: noms de fichiers (avec caractères génériques) dans l'avant-dernier champ. Enfin: OK.
MikeW
8

Copiez et collez ceci pour l'utiliser git find-file SEARCHPATTERN

Impression de toutes les branches recherchées:

git config --global alias.find-file '!for branch in `git for-each-ref --format="%(refname)" refs/heads`; do echo "${branch}:"; git ls-tree -r --name-only $branch | nl -bn -w3 | grep "$1"; done; :'

Imprimer uniquement les branches avec des résultats:

git config --global alias.find-file '!for branch in $(git for-each-ref --format="%(refname)" refs/heads); do if git ls-tree -r --name-only $branch | grep "$1" > /dev/null; then  echo "${branch}:"; git ls-tree -r --name-only $branch | nl -bn -w3 | grep "$1"; fi; done; :'

Ces commandes vont ajouter des scripts shell minimum directement à votre ~/.gitconfigcomme alias global git .

lumbric
la source
il recherche uniquement dans la branche principale
Nasif Imtiaz Ohi
Il recherche dans toutes les branches locales
Leonardo Herrera