Comment vérifier s'il n'y a rien à commettre dans la branche actuelle?

172

Le but est d'obtenir un statut sans ambiguïté qui peut être évalué dans une commande shell.

J'ai essayé git statusmais il renvoie toujours 0, même s'il y a des éléments à valider.

git status
echo $?  #this is always 0

J'ai une idée mais je pense que c'est plutôt une mauvaise idée.

if [ git status | grep -i -c "[a-z]"> 2 ];
then
 code for change...
else
  code for nothing change...
fi

d'une autre manière?


mise à jour avec la résolution suivante, voir l'article de Mark Longair

J'ai essayé cela mais cela pose un problème.

if [ -z $(git status --porcelain) ];
then
    echo "IT IS CLEAN"
else
    echo "PLEASE COMMIT YOUR CHANGE FIRST!!!"
    echo git status
fi

J'obtiens l'erreur suivante [: ??: binary operator expected

maintenant, je regarde l'homme et j'essaye le git diff.

=================== code pour mon espoir, et j'espère mieux répondre =======================

#if [ `git status | grep -i -c "$"` -lt 3 ];
# change to below code,although the above code is simple, but I think it is not strict logical
if [ `git diff --cached --exit-code HEAD^ > /dev/null && (git ls-files --other --exclude-standard --directory | grep -c -v '/$')` ];
then
        echo "PLEASE COMMIT YOUR CHANGE FIRST!!!"
    exit 1

else
    exit 0
fi
9nix00
la source
4
Dans la section mise à jour, il semble que vous ne faites pas réellement ce que suggère eckes dans sa réponse - comme il le dit, vous devez mettre des guillemets autour du $(git status --porcelain). De plus, si vous souhaitez mettre des points d'exclamation dans votre message, vous devrez utiliser des guillemets simples plutôt que des guillemets doubles - c'est-à-dire qu'il devrait l'être à la echo 'PLEASE COMMIT YOUR CHANGE FIRST!!!'place
Mark Longair
4
comme le dit Mark: vous devez mettre des guillemets doubles autour du$(git status --porcelain) , comme je vous l'ai dit!
eckes
1
Ces questions seraient beaucoup plus utiles si elles n'incluaient pas des parties de réponses.
oberlies
@ 9nix00 faites ce qu'on vous a dit et éditez et corrigez le bogue dans votre script shell ci-dessus: BUG: if [-z $ (une commande)] FIX: if [-z "$ (une commande)"]
MarcH

Réponses:

232

Une alternative au test si la sortie de git status --porcelainest vide consiste à tester séparément chaque condition qui vous intéresse. On peut ne pas toujours se soucier, par exemple, s'il y a des fichiers non suivis dans la sortie de git status.

Par exemple, pour voir s'il existe des modifications locales non organisées, vous pouvez consulter le code de retour de:

git diff --exit-code

Pour vérifier s'il y a des modifications qui sont préparées mais non validées, vous pouvez utiliser le code de retour de:

git diff --cached --exit-code

Enfin, si vous voulez savoir s'il existe des fichiers non suivis dans votre arbre de travail qui ne sont pas ignorés, vous pouvez tester si la sortie de la commande suivante est vide:

git ls-files --other --exclude-standard --directory

Mise à jour: Vous demandez ci-dessous si vous pouvez modifier cette commande pour exclure les répertoires de la sortie. Vous pouvez exclure des répertoires vides en ajoutant --no-empty-directory, mais pour exclure tous les répertoires de cette sortie, je pense que vous devrez filtrer la sortie, par exemple avec:

git ls-files --other --exclude-standard --directory | egrep -v '/$'

Le -và egrepsignifie pour afficher uniquement les lignes qui ne correspondent pas au modèle, et le modèle correspond à toute ligne qui se termine par un /.

Mark Longair
la source
J'ai penché ces conseils et j'ai un problème. autrement dit, utilisez git ls-files --other --exclude-standard --directory obtenir la liste des répertoires d'inclusion. y a-t-il une manière d'exclure ces répertoires?
9nix00
oui, c'est ce que je veux. et je mets à jour mon message pour un nouveau code de script. Je pense que le vôtre suggère est plus logique, bien que plus de code lol ... et Hope de meilleures réponses apparaissent.
9nix00
3
@albfan: c'est dans la page de manuel de git-diff : "Faites que le programme se termine avec des codes similaires à diff (1). Autrement dit, il sort avec 1 s'il y avait des différences et 0 signifie pas de différences."
Mark Longair
Juste pour signaler, il est là au moins depuis 2007 13da0fc0 , vraiment pratique pour les scripts shell, et entièrement compatible avec les anciennes versions de git
albfan
10
--quiet(ce qui implique --exit-code) fait également taire la sortie, pour ceux qui ne veulent que le code de sortie.
phs
113

La valeur de retour de git statusvous indique simplement le code de sortie de git status, pas s'il y a des modifications à valider.

Si vous voulez une version plus lisible par ordinateur de la git statussortie, essayez

git status --porcelain

Voir la description de git statuspour plus d'informations à ce sujet.

Exemple d'utilisation (le script teste simplement s'il git status --porcelaindonne une sortie, aucune analyse nécessaire):

if [ -n "$(git status --porcelain)" ]; then
  echo "there are changes";
else
  echo "no changes";
fi

Veuillez noter que vous devez citer la chaîne à tester, c'est-à-dire la sortie de git status --porcelain. Pour plus d'informations sur les constructions de test, reportez-vous au Advanced Bash Scripting Guide (Section comparaison de chaînes ).

eckes
la source
salut, vous donnez une bonne suggestion, je l'ai essayé, mais dans le script, cela pose un problème, je l'ai amélioré, si nous l'utilisons si [-z $ (git status --porcelain)]; il obtiendra une erreur, [: ??: opérateur binaire attendu Je trouve le manuel et l'utilise si [-z $ (git status --short)]; cela peut fonctionner, merci!
9nix00
désolé, il y a toujours un problème de cause. lorsque le commit est propre. utiliser de la porcelaine et court les deux ok. mais quand le commit n'est pas propre. cela provoquera une erreur. [: ??: opérateur binaire attendu. Je pense que nous devrions peut-être essayer d'utiliser base64 pour l'encoder. Laisse-moi essayer! téléchargement des outils de commande base64 .... lol
9nix00
cela pose problème lorsque le commit n'est pas propre.
9nix00
1
Excellente solution. Pour plus de robustesse, vous pouvez ajouter || echo noà la substitution de commande afin que l'espace de travail ne soit pas signalé par erreur propre en cas d' git statuséchec fondamental. De plus, votre code est (louable) conforme à POSIX, mais puisque vous créez un lien vers un guide bash, permettez-moi d'ajouter que si vous utilisez bash au [[ ... ]]lieu de compatible POSIX [ ... ], vous n'avez pas besoin de double-guillemet la substitution de commande (même si elle ne fait pas de mal): [[ -z $(git status --porcelain) ]].
mklement0
@eckes J'ai commencé un nouveau repo il y a quelques jours, j'ai ajouté un commit, j'ai essayé d'écrire un hook pré-commit et de vérifier ce qui doit être commis et ce que vous dites n'a pas fonctionné sur mon cas.
alinsoar
33

Si vous êtes comme moi, vous voulez savoir s'il y a:

1) les modifications apportées aux fichiers existants 2) les fichiers nouvellement ajoutés 3) les fichiers supprimés

et spécifiquement ne veulent pas connaître 4) les fichiers non suivis.

Cela devrait le faire:

git status --untracked-files=no --porcelain

Voici mon code bash pour quitter le script si le dépôt est propre. Il utilise la version courte de l'option des fichiers non suivis:

[[ -z $(git status -uno --porcelain) ]] && echo "this branch is clean, no need to push..." && kill -SIGINT $$;
humeur
la source
4
+1 pour —untracked-files=no; Je pense que votre test peut être simplifié [[ -z $(git status --untracked-files=no --porcelain) ]]. git statusne devrait pas écrire à stderr, à moins que quelque chose va mal fondamental - et vous ne voulez voir que la production. (Si vous souhaitez un comportement plus robuste dans cet événement, ajoutez || echo noà la substitution de commande afin que le test de propreté échoue toujours). Comparaisons de chaînes / l' -zopérateur peut gérer des chaînes multilignes - pas besoin de tail.
mklement0
2
merci @ mklement0, encore un peu plus court:[[ -z $(git status -u no --porcelain) ]]
moodboom
1
correction: ma version plus courte vérifie en fait juste le statut du fichier nommé "non"! Bzzt. Devrait être: [[ -z $(git status -uno --porcelain) ]]
moodboom
2
J'apprécie le suivi; c'est un bogue subtil - la leçon est que les options courtes avec des arguments optionnels doivent avoir l'argument ajouté directement , sans espace entre les deux. Que diriez-vous d'incorporer la version courte corrigée directement dans votre réponse?
mklement0
9

Il est possible de combiner git status --porcelainavec un simple greppour effectuer le test.

if git status --porcelain | grep .; then
    echo Repo is dirty
else
    echo Repo is clean
fi

J'utilise parfois cela comme un simple one-liner:

# pull from origin if our repo is clean
git status --porcelain | grep . || git pull origin master

Ajoutez -qsà votre commande grep pour la rendre silencieuse.

Steve Prentice
la source
2
+1 pour l'élégance; petite mise en garde: devrait git statuséchouer fatalement (par exemple, un dépôt corrompu), votre test signalera par erreur un espace de travail propre . Une option est d'utiliser git status --porcelain 2>&1, mais cela «mangerait» le message d'erreur si vous utilisiez grep avec -q. (Faire face à qui perdre l'élégance: (git status --porcelain || echo err) | grep -q .)
mklement0
alternativement, on peut écrire:test -z "$(git status --porcelain)" || git pull origin master
VasiliNovikov
5

À partir du code source de git, il existe un script sh qui comprend les éléments suivants.

require_clean_work_tree () {
    git rev-parse --verify HEAD >/dev/null || exit 1
    git update-index -q --ignore-submodules --refresh
    err=0

    if ! git diff-files --quiet --ignore-submodules
    then
        echo >&2 "Cannot $1: You have unstaged changes."
        err=1
    fi

    if ! git diff-index --cached --quiet --ignore-submodules HEAD --
    then
        if [ $err = 0 ]
        then
            echo >&2 "Cannot $1: Your index contains uncommitted changes."
        else
            echo >&2 "Additionally, your index contains uncommitted changes."
        fi
        err=1
    fi

    if [ $err = 1 ]
    then
        test -n "$2" && echo >&2 "$2"
        exit 1
    fi
}

Ce sniplet montre comment il est possible d'utiliser git diff-fileset git diff-indexde savoir s'il y a des modifications aux fichiers précédemment connus. Il ne vous permet cependant pas de savoir si un nouveau fichier inconnu a été ajouté à l'arborescence de travail.

Maître des flèches
la source
cela fonctionne bien sauf pour les nouveaux fichiers. nous pouvons ajouter ceci. si ! git ls-files --other --exclude-standard --directory | grep -c -v '/ $' puis exit 0 else echo "Veuillez valider votre nouveau fichier, si vous ne voulez pas l'ajouter, veuillez l'ajouter dans le fichier git-ignore." sortie 1 fi
9nix00
Juste if [ -n "$(git ls-files --others --exclude-standard)" ]sans aucun piping ou greping supplémentaire devrait être suffisant pour détecter les fichiers non suivis.
Arrowmaster
5

je ferais un test sur ceci:

git diff --quiet --cached

ou ceci pour être explicite:

git diff --quiet --exit-code --cached

où:

--exit-code

Faites quitter le programme avec des codes similaires à diff (1). Autrement dit, il sort avec 1 s'il y avait des différences et 0 signifie aucune différence.

--silencieux

Désactivez toutes les sorties du programme. Implique --exit-code

archf
la source
3

Je suis un peu en retard dans la discussion, mais si c'est seulement que vous devez avoir un code de sortie de 0 si git status --porcelainrien ne retourne et! = 0 sinon, essayez ceci:

exit $( git status --porcelain | wc -l )

Cela fera du nombre de lignes le code de sortie, au risque d'avoir des problèmes quand il y a plus de 255 lignes. Alors

exit $( git status --porcelain | head -255 | wc -l )

en rendra compte;)

Skeeve
la source
1
Ce sera fondamentalement indéfini s'il y a plus de 255 lignes de sortie.
tripleee
Bien repéré, merci!
Skeeve
2

J'utilise ceci dans un script pour avoir:

  • 0 quand tout est propre
  • 1 quand il y a un diff ou des fichiers non suivis

    [-z "$ (git status --porcelain)"]

plus grossier
la source
l'utilisation if ! git diff --quiet; thenest plus propre et plus performante (je pense). En d'autres termes, utilisez le code de sortie, pas la sortie standard.
Alexander Mills
1
@AlexanderMills git diff --quietse comporte différemment des git status --porcelainmodifications mises en cache.
Martin von Wittich le
0

Pas joli, mais fonctionne:

git status | grep -qF 'working directory clean' || echo "DIRTY"

Je ne sais pas si le message dépend de la langue, alors peut-être mettre un LANG=Cdevant.

Tilman Vogel
la source