89 votes

Puis-je utiliser Git pour rechercher les noms de fichiers correspondants dans un référentiel ?

Dites simplement que j'ai un dossier : "HelloWorld.pm" dans plusieurs sous-répertoires d'un dépôt Git.

J'aimerais lancer une commande pour trouver les chemins complets de tous les fichiers correspondant à "HelloWorld.pm" :

Par exemple :

/path/to/repository/HelloWorld.pm
/path/to/repository/but/much/deeper/down/HelloWorld.pm
/path/to/repository/please/dont/make/me/search/through/the/lot/HelloWorld.pm

Comment puis-je utiliser Git pour trouver efficacement tous les chemins complets qui correspondent à un nom de fichier donné ?

Je sais que je peux le faire avec la commande find de Linux/Unix, mais j'espérais éviter de parcourir tous les sous-répertoires à la recherche d'instances du nom de fichier.

125voto

Brian Campbell Points 101107

git ls-files vous donnera une liste de tous les fichiers dans l'état actuel du référentiel (le cache ou l'index). Vous pouvez passer un motif pour obtenir les fichiers correspondant à ce motif.

git ls-files HelloWorld.pm '**/HelloWorld.pm'

Si vous souhaitez trouver un ensemble de fichiers et en extraire le contenu, vous pouvez le faire avec git grep :

git grep some-string -- HelloWorld.pm '**/HelloWorld.pm'

47voto

Uwe Geuder Points 925

Hmm, la question initiale concernait le dépôt. Un dépôt contient plus d'un commit (dans le cas général au moins), mais les réponses données précédemment ne portent que sur un seul commit.

Parce que je n'ai pas trouvé de réponse qui recherche vraiment l'ensemble de l'historique des commits, j'ai écrit un rapide script git-find-by-name qui prend (presque) tous les commits en considération.

#! /bin/sh
tmpdir=$(mktemp -td git-find.XXXX)
trap "rm -r $tmpdir" EXIT INT TERM

allrevs=$(git rev-list --all)
# well, nearly all revs, we could still check the log if we have
# dangling commits and we could include the index to be perfect...

for rev in $allrevs
do
  git ls-tree --full-tree -r $rev >$tmpdir/$rev 
done

cd $tmpdir
grep $1 * 

Il existe peut-être une méthode plus élégante.

Veuillez noter la façon triviale dont le paramètre est passé à grep, de sorte qu'il correspondra à des parties du nom de fichier. Si cela n'est pas souhaité, ancrez votre expression de recherche et/ou ajoutez des options grep appropriées.

Pour les histoires profondes, la sortie pourrait être trop bruyante, j'ai pensé à un script qui convertit une liste de révisions en un intervalle, comme l'opposé de ce que git rev-list peut faire. Mais pour l'instant, c'est resté à l'état d'idée.

28voto

Greg Hewgill Points 356191

Essayez :

git ls-tree -r HEAD | grep HelloWorld.pm

9voto

Bull Points 466
git ls-files | grep -i HelloWorld.pm

L'option grep -i rend grep insensible à la casse.

4voto

Dean Hall Points 111

(C'est un peu un abus de commentaire, je l'admets, mais je ne peux pas encore commenter et j'ai pensé améliorer la réponse de @uwe-geuder).

#!/bin/bash
#
#

# I'm using a fixed string here, not a regular expression, but you can easily
# use a regular expression by altering the call to grep below.
name="$1"

# Verify usage.
if [[ -z "$name" ]]
then
    echo "Usage: $(basename "$0") <file name>" 1>&2
    exit 100
fi  

# Search all revisions; get unique results.
while IFS= read rev
do
    # Find $name in $rev's tree and only use its path.
    grep -F -- "$name" \
        <(git ls-tree --full-tree -r "$rev" | awk '{ print $4 }')
done < \
    <(git rev-list --all) \
    | sort -u

Encore une fois, +1 à @uwe-geuder pour une excellente réponse.

Si vous êtes intéressé par la BASH elle-même :

À moins que vous ne soyez assuré de la séparation des mots dans une boucle for (comme lors de l'utilisation d'un tableau tel que celui-ci) : for item in "${array[@]}" ), je recommande vivement d'utiliser while IFS= read var ; do ... ; done < <(command) lorsque la sortie de la commande sur laquelle vous bouclez est séparée par des nouvelles lignes (ou read -d'' lorsque la sortie est séparée par la chaîne null $'\0' ). Tandis que les git rev-list --all est assuré d'utiliser des chaînes hexadécimales de 40 octets (sans espaces), mais je n'aime pas prendre de risques. Je peux maintenant facilement changer la commande de git rev-list --all à toute commande qui produit des lignes

Je recommande également d'utiliser les mécanismes BASH intégrés pour injecter des données d'entrée et filtrer les données de sortie au lieu d'utiliser des fichiers temporaires.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X