263 votes

Vérification d'un index sale ou de fichiers non suivis avec Git

Comment puis-je vérifier si j'ai des modifications non validées dans mon dépôt git :

  1. Modifications ajoutées à l'index mais non validées
  2. Fichiers non suivis

à partir d'un script ?

git-status semble toujours retourner zéro avec la version 1.6.4.2 de git.

3 votes

Git status retournera 1 s'il y a des fichiers modifiés non paginés. Mais en général, je trouve que les outils git ne sont pas particulièrement minutieux avec le retour du statut. EG git diff renvoie 0, qu'il y ait ou non des différences.

11 votes

@intuited : Si vous avez besoin diff pour indiquer la présence ou l'absence de différences plutôt que l'exécution réussie de la commande, alors vous devez utiliser --exit-code o --quiet . les commandes git sont généralement très cohérentes avec le retour d'un code de sortie zéro ou non zéro pour indiquer le succès de la commande.

1 votes

Charles Bailey : Super, j'ai manqué cette option. Merci ! Je suppose que je n'ai jamais vraiment eu besoin de le faire, sinon j'aurais probablement parcouru la page de manuel pour cela. Content que vous m'ayez corrigé :)

434voto

Chris Johnsen Points 50064

La clé d'un "scripting" fiable de Git est d'utiliser les commandes de "plomberie".

Les développeurs prennent soin, lorsqu'ils modifient les commandes de plomberie, de s'assurer qu'elles fournissent des interfaces très stables (c'est-à-dire qu'une combinaison donnée d'état du dépôt, de stdin, d'options de ligne de commande, d'arguments, etc. produira la même sortie dans toutes les versions de Git où la commande/option existe). De nouvelles variations de sortie dans les commandes de plomberie peuvent être introduites via de nouvelles options, mais cela ne peut pas introduire de problèmes pour les programmes qui ont déjà été écrits contre des versions plus anciennes (ils n'utiliseraient pas les nouvelles options, puisqu'elles n'existaient pas (ou du moins n'étaient pas utilisées) au moment où le script a été écrit).

Malheureusement, les commandes Git "de tous les jours" sont les commandes "en porcelaine", de sorte que la plupart des utilisateurs de Git ne sont pas familiers avec les commandes de plomberie. La distinction entre les commandes "porcelaine" et les commandes "plomberie" est faite dans les principaux éléments suivants page de manuel git (voir les sous-sections intitulées Commandes de haut niveau (porcelaine) y Commandes de bas niveau (plomberie) .


Pour vous renseigner sur les modifications non admises, vous aurez probablement besoin de git diff-index (comparer l'index (et peut-être les éléments suivis de l'arbre de travail) avec d'autres arbres (par ex. HEAD )), peut-être git diff-files (comparer l'arbre de travail à l'index), et éventuellement git ls-files (liste des fichiers ; par exemple, liste des fichiers non suivis, non ignorés).

(Notez que dans les commandes ci-dessous, HEAD -- est utilisé à la place de HEAD car sinon la commande échoue s'il existe un fichier nommé HEAD .)

Pour vérifier si un dépôt a des changements indexés (pas encore livrés), utilisez ceci :

git diff-index --quiet --cached HEAD --
  • S'il sort avec 0 puis il n'y a pas eu de différences ( 1 signifie qu'il y avait des différences).

Pour vérifier si un arbre de travail a des changements qui pourraient être mis en scène :

git diff-files --quiet
  • Le code de sortie est le même que pour git diff-index ( 0 == pas de différences ; 1 == différences).

Pour vérifier si la combinaison de l'index et des fichiers suivis dans l'arborescence de travail a changé par rapport à HEAD :

git diff-index --quiet HEAD --
  • C'est comme une combinaison des deux précédentes. La principale différence est qu'il ne signalera toujours "aucune différence" si vous avez une modification échelonnée que vous avez "annulée" dans l'arbre de travail (retour au contenu qui se trouve dans l'arbre de travail). HEAD ). Dans cette même situation, les deux commandes distinctes renverraient toutes deux le rapport "différences présentes".

Vous avez également mentionné les fichiers non tracés. Vous pouvez vouloir dire "non suivis et non ignorés", ou vous pouvez vouloir dire simplement "non suivis" (y compris les fichiers ignorés). Dans tous les cas, git ls-files est l'outil idéal pour ce travail :

Pour "untracked" (inclura les fichiers ignorés, s'ils sont présents) :

git ls-files --others

Pour "sans trace et sans signe" :

git ls-files --exclude-standard --others

Ma première idée est de vérifier si ces commandes ont un résultat :

test -z "$(git ls-files --others)"
  • S'il sort avec 0 alors il n'y a pas de fichiers non tracés. S'il sort avec 1 alors il y a des fichiers non tracés.

Il y a une petite chance que cela se traduise par des sorties anormales de git ls-files en rapports "no untracked files" (les deux résultent en des sorties non nulles de la commande ci-dessus). Une version un peu plus robuste pourrait ressembler à ceci :

u="$(git ls-files --others)" && test -z "$u"
  • L'idée est la même que la commande précédente, mais elle permet aux erreurs inattendues de git ls-files pour se propager à l'extérieur. Dans ce cas, une sortie non nulle pourrait signifier "il y a des fichiers non suivis" ou cela pourrait signifier qu'une erreur s'est produite. Si vous voulez que le résultat "erreur" soit combiné avec le résultat "pas de fichiers non suivis" à la place, utilisez test -n "$u" (où la sortie de 0 signifie "quelques fichiers non suivis", et un chiffre différent de zéro signifie une erreur ou "aucun fichier non suivi").

Une autre idée est d'utiliser --error-unmatch pour provoquer une sortie non nulle lorsqu'il n'y a pas de fichiers non suivis. Cela présente également le risque de confondre "aucun fichier non suivi" (exit 1 ) avec "une erreur s'est produite" (sortie non nulle, mais probablement 128 ). Mais la vérification de 0 vs. 1 par rapport aux codes de sortie non nuls est probablement assez robuste :

git ls-files --others --error-unmatch . >/dev/null 2>&1; ec=$?
if test "$ec" = 0; then
    echo some untracked files
elif test "$ec" = 1; then
    echo no untracked files
else
    echo error from ls-files
fi

Tout ce qui précède git ls-files les exemples peuvent prendre --exclude-standard si vous voulez considérer uniquement les fichiers non suivis et non ignorés.

6 votes

Je tiens à souligner que git ls-files --others donne local fichiers non suivis, tandis que le git status --porcelain de la réponse acceptée donne tous les fichiers non suivis qui sont sous le dépôt git. Je ne sais pas laquelle de ces réponses l'auteur de l'original voulait, mais la différence entre les deux est intéressante.

0 votes

Sur Ubuntu 10.04, git ls-files --other --error-unmatch --exclude-standard retourne toujours 0, peu importe si j'ai des fichiers non suivis. Est-ce que quelque chose m'échappe ?

1 votes

@phunehehehe : Vous devez fournir un pathpec avec --error-unmatch . Essayez (par exemple) git ls-files --other --error-unmatch --exclude-standard . (notez le point de fin, il fait référence au cwd ; exécutez cette commande depuis le répertoire de premier niveau de l'arbre de travail).

179voto

0xfe Points 2844

Bon timing ! J'ai écrit un article de blog sur ce sujet il y a quelques jours, lorsque j'ai découvert comment ajouter des informations sur le statut de git à mon invite.

Voilà ce que je fais :

  1. Pour l'état sale :

    # Returns "*" if the current git branch is dirty.
    function evil_git_dirty {
      [[ $(git diff --shortstat 2> /dev/null | tail -n1) != "" ]] && echo "*"
    }
  2. Pour les fichiers non suivis (Notez le --porcelain pour git status ce qui vous donne une sortie agréable à analyser) :

    # Returns the number of untracked files
    
    function evil_git_num_untracked_files {
      expr `git status --porcelain 2>/dev/null| grep "^??" | wc -l` 
    }

Bien que git diff --shortstat est plus pratique, vous pouvez également utiliser git status --porcelain pour récupérer les fichiers sales :

# Get number of files added to the index (but uncommitted)
expr $(git status --porcelain 2>/dev/null| grep "^M" | wc -l)

# Get number of files that are uncommitted and not added
expr $(git status --porcelain 2>/dev/null| grep "^ M" | wc -l)

# Get number of total uncommited files
expr $(git status --porcelain 2>/dev/null| egrep "^(M| M)" | wc -l)

Note : Le 2>/dev/null filtre les messages d'erreur afin que vous puissiez utiliser ces commandes sur des répertoires non git. (Elles renverront simplement 0 pour le décompte des fichiers).

Modifier :

Voici les postes :

Ajout d'informations sur l'état de Git à l'invite de votre terminal

Amélioration de l'invite Shell compatible avec Git

8 votes

Il est intéressant de noter que la complétion de git bash est fournie avec une fonction shell permettant de faire à peu près ce que vous faites avec votre prompt - __git_ps1 . Il affiche les noms des branches, y compris un traitement spécial si vous êtes en train de faire un rebasement, un am-apply, une fusion ou un bisect. Et vous pouvez définir la variable d'environnement GIT_PS1_SHOWDIRTYSTATE pour obtenir un astérisque pour les changements non indexés et un plus pour les changements indexés. (Je pense que l'on peut aussi l'utiliser pour indiquer les fichiers non suivis, et vous donner un peu de git-describe sortie)

8 votes

Un avertissement : git diff --shortstat donnera un faux négatif si des changements sont déjà dans l'index.

4 votes

git status --porcelain est préférable, car git diff --shortstat n'attrapera pas les fichiers vides nouvellement créés. Vous pouvez l'essayer dans n'importe quel arbre de travail propre : touch foo && git diff --shortstat

155voto

benzado Points 36367

En supposant que vous êtes sur git 1.7.0 ou plus...

Après avoir lu toutes les réponses sur cette page et après avoir fait quelques essais, je pense que la méthode qui présente la bonne combinaison entre exactitude et brièveté est la suivante :

test -n "$(git status --porcelain)"

Bien que git permette beaucoup de nuances entre ce qui est suivi, ignoré, non suivi mais non ignoré, et ainsi de suite, je crois que le cas d'utilisation typique est l'automatisation de build scripts, où vous voulez tout arrêter si votre checkout n'est pas propre.

Dans ce cas, il est logique de simuler ce que le programmeur ferait : tapez git status et regardez la sortie. Mais nous ne voulons pas compter sur l'apparition de mots spécifiques, nous utilisons donc la fonction --porcelain mode introduit dans la version 1.7.0 ; lorsqu'il est activé, un répertoire propre n'entraîne aucune sortie.

Ensuite, nous utilisons test -n pour voir s'il y a eu une sortie ou pas.

Cette commande retournera 1 si le répertoire de travail est propre et 0 s'il y a des changements à valider. Vous pouvez modifier le -n à un -z si vous voulez le contraire. C'est utile pour enchaîner cela à une commande dans un script. Par exemple :

test -z "$(git status --porcelain)" || red-alert "UNCLEAN UNCLEAN"

Ceci dit effectivement "soit il n'y a pas de changements à faire, soit il faut déclencher une alarme" ; cette ligne unique peut être préférable à une déclaration if selon le script que vous écrivez.

1 votes

Pour moi, toutes les autres commandes donnaient des résultats différents sur le même représentant entre Linux et Windows. Cette commande m'a donné le même résultat dans les deux cas.

9 votes

Merci d'avoir répondu à la question et de ne pas avoir divagué, sans jamais fournir de réponse claire.

0 votes

Pour un déploiement manuel script, combinez ceci avec test -n "$(git diff origin/$branch)" pour aider à empêcher les commits locaux d'être autorisés dans un déploiement.

15voto

Dean Rather Points 7856

Une mise en œuvre de VonC La réponse de la Commission :

if [[ -n $(git status --porcelain) ]]; then echo "repo is dirty"; fi

4voto

VonC Points 414372

Pourquoi ne pas encapsuler ' git status avec un script qui :

  • va analyser la sortie de cette commande
  • retournera le code d'erreur approprié en fonction de ce dont vous avez besoin.

De cette façon, vous pouvez utiliser ce statut "amélioré" dans votre script.


Comme 0xfe mentionne dans son excellente réponse , git status --porcelain est instrumental dans toute solution basée sur script.

--porcelain

Donner la sortie dans un format stable et facile à analyser pour les scripts.
Actuellement, il est identique à --short output mais il est garanti qu'il ne changera pas à l'avenir, ce qui le rend sûr pour les scripts.

0 votes

Parce que je suis paresseux, probablement. Je pensais qu'il y avait une fonction intégrée pour cela, car cela semble être un cas d'utilisation assez fréquent.

0 votes

J'ai posté une solution basée sur votre suggestion, bien que je n'en sois pas extrêmement satisfait.

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