345 votes

Comment puis-je déterminer par programme s'il y a des modifications non validées ?

Dans un Makefile, j'aimerais effectuer certaines actions s'il y a des changements non validés (soit dans l'arbre de travail, soit dans l'index). Quelle est la manière la plus propre et la plus efficace de le faire ? Une commande qui se termine avec une valeur de retour de zéro dans un cas et de non-zéro dans l'autre me conviendrait.

Je peux courir git status et faire passer la sortie par grep mais je pense qu'il doit y avoir un meilleur moyen.

6 votes

376voto

VonC Points 414372

UPDATE : l'OP Daniel Stutzbach souligne dans les commentaires que cette simple commande git diff-index a travaillé pour lui :

git update-index --refresh 
git diff-index --quiet HEAD --

Une option plus précise serait de tester git status --porcelain=v1 2>/dev/null | wc -l en utilisant le porcelain option .
Voir Myridium 's réponse .

( nornagon mentionne dans les commentaires que, s'il y a des fichiers qui ont été touchés, mais dont le contenu est le même que dans l'index, vous devrez exécuter git update-index --refresh avant git diff-index sinon diff-index signalera à tort que l'arbre est sale)

Vous pouvez alors voir " Comment vérifier si une commande a réussi ? " si vous l'utilisez dans un script bash :

git diff-index --quiet HEAD -- || echo "untracked"; // do something about it

Note : comme a commenté par Anthony Sottile

git diff-index HEAD ... échouera sur une branche qui n'a pas de commits (comme un dépôt nouvellement initialisé).
Une solution que j'ai trouvée est git diff-index $(git write-tree) ...

Et haridsv souligne dans les commentaires que git diff-files sur un nouveau ne le détecte pas comme un diff.
L'approche la plus sûre semble être d'exécuter git add sur la spécification du fichier d'abord et ensuite utiliser git diff-index pour voir si quelque chose a été ajouté à l'index avant de lancer git commit .

git add ${file_args} && \
git diff-index --cached --quiet HEAD || git commit -m '${commit_msg}'

Et 6502 dans les commentaires :

Un problème auquel je me suis heurté est que git diff-index dira qu'il y a des différences alors qu'il n'y en a aucune, sauf pour les horodatages des fichiers.
Running git diff une fois résout le problème (de manière assez surprenante, git diff modifie effectivement le contenu de la sandbox, ce qui signifie qu'ici .git/index )

Ces problèmes d'horodatage peuvent également se produire si git est exécuté dans un docker .


Réponse originale :

"Par programme", on entend ne comptez jamais sur commandes en porcelaine .
Faites toujours confiance à commandes de plomberie .

Voir aussi " Vérification d'un index sale ou de fichiers non suivis avec Git " pour les alternatives (comme git status --porcelain )

Vous pouvez vous inspirer du nouveau " require_clean_work_tree fonction "qui est écrit au moment où nous parlons ;) (début octobre 2010)

require_clean_work_tree () {
    # Update the index
    git update-index -q --ignore-submodules --refresh
    err=0

    # Disallow unstaged changes in the working tree
    if ! git diff-files --quiet --ignore-submodules --
    then
        echo >&2 "cannot $1: you have unstaged changes."
        git diff-files --name-status -r --ignore-submodules -- >&2
        err=1
    fi

    # Disallow uncommitted changes in the index
    if ! git diff-index --cached --quiet HEAD --ignore-submodules --
    then
        echo >&2 "cannot $1: your index contains uncommitted changes."
        git diff-index --cached --name-status -r --ignore-submodules HEAD -- >&2
        err=1
    fi

    if [ $err = 1 ]
    then
        echo >&2 "Please commit or stash them."
        exit 1
    fi
}

12 votes

Le principe de la "plomberie contre la porcelaine pour les scripts" est une leçon à retenir. Jakub Narebski qui m'a été mentionné à plusieurs reprises : " Comment lister tous les logs du projet en cours dans git ? ", " git : journal des modifications jour après jour ", ...

19 votes

Après avoir cliqué sur certains des liens que vous suggérez, j'ai trouvé ce que je cherchais : git diff-index --quiet HEAD .

11 votes

@DanielStutzbach : Cela peut échouer si vous avez un fichier appelé HEAD dans le répertoire de travail. Meilleure utilisation git diff-index --quiet HEAD -- .

167voto

Nepthar Points 131

Bien que les autres solutions soient très complètes, si vous voulez quelque chose de vraiment rapide et sale, essayez quelque chose comme ceci :

[[ -z $(git status -s) ]]

Il vérifie simplement s'il y a une sortie dans le résumé d'état.

16 votes

Fonctionne pour moi. utilisez -n pour l'inverse (vous avez des modifications) par exemple ` if [[ -n $(git status -s) ]] ; then ... fi`

0 votes

Cela fonctionne, mais pouvez-vous dire ce que le [[ ... ]] syntaxe est en train de faire ? Je n'ai jamais rien vu de tel auparavant.

0 votes

[[ est un mot-clé de l'interpréteur de commandes ( $ type [[ ) dans bash et la plupart des shells. Il se comporte comme la fonction test commandement. Vous la verrez souvent dans le cadre d'une instruction if : if [[ -f "$my_file" ]]; then echo "found!"; else echo "not found; fi

80voto

Josh Lee Points 53741

git diff --exit-code retournera une valeur non nulle s'il y a des changements ; git diff --quiet est le même, sans sortie. Puisque vous voulez vérifier l'arbre de travail et l'index, utilisez

git diff --quiet && git diff --cached --quiet

Ou

git diff --quiet HEAD

L'un ou l'autre vous dira s'il y a des changements non validés qui sont mis à jour ou non.

7 votes

Ce ne sont pas des équivalents. La commande unique git diff --quite HEAD vous dira seulement si l'arbre de travail est propre, pas si l'index est propre. Par exemple, si file a été changé entre HEAD~ et HEAD, puis après que git reset HEAD~ -- file il sortira toujours 0 même si des changements échelonnés sont présents dans l'index (wt == HEAD, mais index != HEAD).

2 votes

Attention, ceci n'attrape pas les fichiers supprimés de la zone de transit avec git rm, AFAICS.

30 votes

Les nouveaux fichiers (non-traqués) ne sont pas détectés par git diff --quiet && git diff --cached --quiet .

33voto

Travis R Points 8935

Développant la réponse de @Nepthar :

if [[ -z $(git status -s) ]]
then
  echo "tree is clean"
else
  echo "tree is dirty, please commit changes before running this"
  exit
fi

1 votes

C'est une bonne chose ; je l'utilise pour valider automatiquement des fichiers uniques en testant $(git status -s "$file") et ensuite dans le else clause git add "$file"; git commit -m "your autocommit process" "$file"

2 votes

Si vous faites cela git status -s un git status --porcelain ; git clean -nd Au lieu de cela, les répertoires inutiles, qui sont invisibles pour l'utilisateur, seront également mis en évidence. git status .

31voto

Myridium Points 211

Certaines réponses sont à la fois trop compliquées et ne permettent pas d'atteindre le résultat souhaité. Par exemple, la réponse acceptée ne tient pas compte des fichiers non suivis.

Utilisez les git status --porcelain qui est conçu pour être analysé par une machine bien que certaines personnes aient affirmé (à tort) le contraire dans les commentaires. Si quelque chose apparaît dans git status alors c'est à ce moment-là que je considère que le répertoire de travail est sale. Je teste donc la propreté avec le test [ -z "$(git status --porcelain=v1 2>/dev/null)" ] qui passera également si elle est exécutée en dehors d'un répertoire git.

Exemple de travail minimum :

[ -z "$(git status --porcelain=v1 2>/dev/null)" ] && echo "git undirty"

Tout ce qui apparaît dans git status (à partir de maintenant) déclenchera ce test correctement. Le site =v1 assure un format de sortie cohérent entre les différentes versions de git.


Extra : compter les fichiers sales

Inspiré par cette réponse . Vous grep les lignes de git status --porcelain=v1 sortie. Les deux premiers caractères de chaque ligne indiquent l'état du fichier en question. Après l'extraction, vous pouvez compter combien de fichiers ont ce statut en envoyant la sortie à wc -l qui compte le nombre de lignes.

Par exemple, ce script imprimera certaines informations s'il est exécuté dans un dépôt git.

#!/bin/sh
GS=$(git status --porcelain=v1 2>/dev/null) # Exit code 128 if not in git directory. Unfortunately this exit code is a bit generic but it should work for most purposes.
if [ $? -ne 128 ]; then
  function _count_git_pattern() {
    echo "$(grep "^$1" <<< $GS | wc -l)" 
  }                                           
  echo "There are $(_count_git_pattern "??") untracked files."                                 
  echo "There are $(_count_git_pattern " M") unstaged, modified files."
  echo "There are $(_count_git_pattern "M ")   staged, modified files."        
fi

1 votes

Utilisation intéressante de l'option porcelaine que j'ai présentée en stackoverflow.com/a/6978402/6309 . Cela semble plus exact que ma réponse. Upvoted.

0 votes

@VonC - en effet, j'ai vérifié la documentation git-scm.com/docs/git-status et a remarqué l'option "version". Il y a déjà une v2 mais ce n'est pas la version par défaut. Je sais que d'autres personnes l'ont déjà cité ailleurs dans les commentaires, mais il est dit de l'option --porcelain option : "Donner la sortie dans un format facile à décrypter pour les scripts. Ce format est similaire au format court, mais restera stable à travers les versions de Git et indépendamment de la configuration de l'utilisateur."

0 votes

Oui, la v2 a été introduite dans Git 2.11 ( github.com/git/git/commit/ ), août 2016.

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