Déterminer si le répertoire de travail Git est propre à partir d'un script

84

J'ai un script qui s'exécute rsyncavec un répertoire de travail Git comme destination. Je veux que le script ait un comportement différent selon que le répertoire de travail est propre (aucune modification à valider) ou non. Par exemple, si le résultat de git statusest comme ci-dessous, je veux que le script se ferme:

git status
Already up-to-date.
# On branch master
nothing to commit (working directory clean)
Everything up-to-date

Si le répertoire n'est pas propre, j'aimerais qu'il exécute quelques commandes supplémentaires.

Comment puis-je vérifier la sortie comme ci-dessus dans un script shell?

Brentwpeterson
la source
Est-ce que vérifier un statut depuis la dernière commande peut aider ici? ($?)
UVV
Pourriez-vous donner plus de détails s'il vous plaît? Quelle est l'idée principale de votre script?
Tachomi
@tachomi j'ai ajouté le contexte dans l'édition
brentwpeterson
vous pouvez simplement supposer que ce n'est pas propre et faire un git reset --hard origin/branchsi si c'est ce que vous allez ... comme si vous essayez de nettoyer après avoir compilé quelque chose, etc.
SnakeDoc
1
@ SnakeDoc Vous pourriez le faire, mais je suppose que le cas inverse serait plus courant, c'est-à-dire quitter si le répertoire de travail est sale pour éviter de modifier les modifications locales. Considérer les deux cas rendrait la question plus utile pour les futurs lecteurs.
Thomas Nyman

Réponses:

135

Analyser la sortie de git statusest une mauvaise idée car la sortie est destinée à être lisible par l'homme, pas lisible par une machine. Rien ne garantit que la sortie restera la même dans les futures versions de Git ou dans des environnements configurés différemment.

Le commentaire UVV est sur la bonne voie, mais malheureusement, le code de retour de git statusne change pas lorsqu'il y a des modifications non validées. Cependant, il fournit l' --porcelainoption qui git status --porcelainformatera la sortie de dans un format facile à analyser pour les scripts et restera stable à travers les versions de Git et quelle que soit la configuration de l'utilisateur.

Nous pouvons utiliser une sortie vide de git status --porcelaincomme indicateur qu'il n'y a aucune modification à valider:

if [ -z "$(git status --porcelain)" ]; then 
  # Working directory clean
else 
  # Uncommitted changes
fi

Si nous ne nous soucions pas des fichiers non suivis dans le répertoire de travail, nous pouvons utiliser l' --untracked-files=nooption pour ignorer ceux:

if [ -z "$(git status --untracked-files=no --porcelain)" ]; then 
  # Working directory clean excluding untracked files
else 
  # Uncommitted changes in tracked files
fi

Pour rendre plus robuste contre les conditions qui en fait l' origine git statusà l' échec sans sortie stdout, nous pouvons affiner le chèque à:

if output=$(git status --porcelain) && [ -z "$output" ]; then
  # Working directory clean
else 
  # Uncommitted changes
fi

Il est également intéressant de noter que, bien git statusque ne donnant pas de code de sortie significatif lorsque le répertoire de travail est malpropre, git difffournit l' --exit-codeoption qui le rend similaire à l' utilitaire diff , c'est-à-dire quitter avec un statut 1lorsqu'il y avait des différences et 0aucun.

En utilisant cela, nous pouvons vérifier les modifications non mises en scène avec:

git diff --exit-code

et mis en scène, mais pas commis des changements avec:

git diff --cached --exit-code

Bien git diffqu’il soit possible de signaler les fichiers non suivis dans des sous-modules via des arguments appropriés --ignore-submodules, il semble malheureusement qu’il n’ya aucun moyen de le signaler dans le répertoire de travail proprement dit. Si les fichiers non suivis du répertoire de travail sont pertinents, git status --porcelainc'est probablement le meilleur choix.

Thomas Nyman
la source
4
ughhh git status --porcelainse fermera avec le code 0 même s'il y a des modifications non mises en scène pour les fichiers commit et non suivis.
Alexander Mills
J'étais intéressé à déterminer à l'avance si git stashferait quoi que ce soit (il ne génère pas de code retour utile). Je devais ajouter --ignore-submodulescar sinon git statusindiquerait des modifications de sous-modules qui git stashignorent.
Devin Lane
1
@AlexanderMills: J'ai observé la même chose. Mais ensuite vérifié ce qui se if [ -zpassait. Les -zmoyens que si la chaîne suivante est vide, le cas évalue à true. En d'autres termes, si cela ne git status --porcelaindonne aucune chaîne, le référentiel est propre. Sinon, il répertorie les fichiers modifiés / ajoutés / supprimés et n'est plus une chaîne vide. Le ifalors évalue à false.
Adeynack
19

Utilisation:

git diff-index --quiet HEAD

Le code de retour reflète l'état du répertoire de travail (0 = propre, 1 = sale). Les fichiers non suivis sont ignorés.

André van Herk
la source
6
Renvoie 0 lorsqu'il y a des fichiers non suivis dans le répertoire en cours.
Adam Parkin
2
Si des fichiers ont été touchés / écrasés mais sont par ailleurs identiques à l'index, vous devez d'abord les exécuter git update-index --refreshavant git diff-index HEAD. Plus d'infos: stackoverflow.com/q/34807971/1407170
sffc
@AdamParkin Je viens d'ajouter tous les fichiers git add .avant de le publier. Habituellement, c'est la façon de l'utiliser dans un script
Ceztko
C'est bien. Notez qu'un code de retour / sortie différent de zéro est également interprété comme une "erreur". Si vous êtes dans un script avec set -e, votre script se fermera s'il est "sale". Ceci peut être évité en effectuant set +eun appel avant l'appel gitet en ajoutant de set -enouveau après l'évaluation $?.
orion elenzil
1

Extension mineure de l' excellente réponse d' André .

C'est un moyen d'évaluer les résultats et d'éviter un piège si vous êtes dans un script qui a précédemment publié set -e .

Les fichiers non suivis sont ignorés.

set +e
git diff-index --quiet HEAD

if [ $? == 1 ] ; then
  set -e
  GIT_MODS="dirty"
else
  set -e
  GIT_MODS="clean"
fi
orion elenzil
la source