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


227

Dans un Makefile, j'aimerais effectuer certaines actions s'il y a des modifications non validées (soit dans l'arborescence de travail soit dans l'index). Quelle est la façon 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 non nulle dans l'autre conviendrait à mes fins.

Je peux exécuter git statuset diriger la sortie grep, mais je pense qu'il doit y avoir un meilleur moyen.


Réponses:


290

MISE À JOUR : l'OP Daniel Stutzbach souligne dans les commentaires que cette simple commande a git diff-indexfonctionné pour lui:

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

( 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 --refreshavant git diff-index, sinon vous diff-indexrapporterez à tort que l'arborescence 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 commenté par Anthony Sottile

git diff-index HEAD ...échouera sur une branche qui n'a pas de validations (comme un référentiel nouvellement initialisé).
Une solution de contournement que j'ai trouvée estgit diff-index $(git write-tree) ...

Et haridsvsouligne dans les commentaires que git diff-filessur un nouveau fichier ne le détecte pas comme un diff.
L'approche la plus sûre semble être d'exécuter git addd'abord la spécification de fichier, puis de l'utiliser git diff-indexpour voir si quelque chose a été ajouté à l'index avant de s'exécuter git commit.

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

Et 6502 rapports dans les commentaires:

Un problème que j'ai rencontré est celui git diff-indexqui indiquera qu'il y a des différences alors qu'en fait il n'y en a pas, sauf pour les horodatages des fichiers.
Exécuter git diffune fois résout le problème (assez surprenant, git diffcela change en fait le contenu du bac à sable, ce qui signifie ici .git/index)

Ces problèmes d'horodatage peuvent également se produire si git s'exécute dans docker .


Réponse originale:

"Par programme" signifie ne jamais se fier aux commandes en porcelaine .
Comptez toujours sur les commandes de plomberie .

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

Vous pouvez vous inspirer de la nouvelle " require_clean_work_treefonction " qui s'é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
Le principe "plomberie contre porcelaine pour l'écriture de scripts" est une leçon que Jakub Narębski m'a maintes fois mentionnée: " Comment lister tout le journal du projet en cours dans git? ", "
Git

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

11
@DanielStutzbach: Cela pourrait échouer si vous avez un fichier appelé HEADdans le répertoire de travail. Meilleure utilisation git diff-index --quiet HEAD --.
David Ongaro

7
Et pourtant, le manuel aux git status --helpétats: --porcelain Donne la sortie dans un format facile à analyser pour les scripts. Ceci est similaire à la sortie courte, mais restera stable dans toutes les versions de Git et quelle que soit la configuration de l'utilisateur. Voir ci-dessous pour plus de détails.
Ed Randall

7
@VonC qui n'a vraiment aucun sens. De cette façon, vous pouvez tout tourner en sens inverse. - la porcelaine vous donne l'impression qu'elle va bientôt se casser. Si ce n'est pas le cas, cela devrait être appelé plomberie, pas en porcelaine. L'utilisation de --porcelain empêche votre script de se casser, ce qui ne le rend PAS un script en porcelaine ;-). Si vous vouliez que votre script se casse, vous ne devriez pas utiliser --porcelain !!. Donc, cela est complètement incompréhensible et décourage tout le monde.
Xennex81

104

Bien que les autres solutions soient très approfondies, 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.


7
travaille pour moi. utilisez -n pour l'inverse (vous avez des changements) par exemple `if [[-n $ (git status -s)]]; alors ... fi`
aaron

Cela fonctionne, mais pouvez-vous dire ce que [[ ... ]]fait réellement la syntaxe? Je n'ai jamais rien vu de tel auparavant.
GMA

2
@EM le code retour de git statusest en fait ignoré dans ce test. Il ne regarde que la sortie. Consultez cette page correspondante bash pour plus d' informations sur [, [[et comment fonctionne le test en bash.
Nepthar

2
C'est presque la bonne réponse, mais pour le script, il est préférable d'utiliser le --porcelainparamètre comme indiqué ici
Mariusz Pawelski

2
Vous pouvez utiliser git status -s -uallpour inclure des fichiers non suivis.
barfuin

59

git diff --exit-coderetournera différent de zéro s'il y a des changements; git diff --quietest le même sans sortie. Puisque vous voulez vérifier l'arborescence 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 par étapes ou non.


6
Ce ne sont pas équivalents. La commande git diff --quite HEADunique vous indiquera uniquement si l'arborescence de travail est propre, pas si l'index est propre. Par exemple, si a fileété changé entre HEAD ~ et HEAD, puis après git reset HEAD~ -- file, il quittera toujours 0 même s'il y a des changements par étapes présents dans l'index (wt == HEAD, mais index! = HEAD).
Chris Johnsen

2
Attention, cela n'attrapera pas les fichiers supprimés de la zone de transit avec git rm, AFAICS.
nmr

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

17

Développer 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
C'est bon; Je l'utilise pour $(git status -s "$file")elsegit add "$file"; git commit -m "your autocommit process" "$file"
valider automatiquement des

Si vous faites que d' git status -sune git status --porcelain ; git clean -ndplace, les répertoires junk seront ici aussi refait surface, qui sont invisibles à git status.
ecmanaut

4

Comme indiqué dans d'autres réponses, aussi simple qu'une telle commande suffit:

git diff-index --quiet HEAD --

Si vous omettez les deux derniers tirets, la commande échouera si vous avez un fichier nommé HEAD.

Exemple:

#!/bin/bash
set -e
echo -n "Checking if there are uncommited changes... "
trap 'echo -e "\033[0;31mFAILED\033[0m"' ERR
git diff-index --quiet HEAD --
trap - ERR
echo -e "\033[0;32mAll set!\033[0m"

# continue as planned...

Attention: cette commande ignore les fichiers non suivis.


2
Comme indiqué dans les commentaires sur cette réponse, cela ne détecte pas les fichiers nouvellement ajoutés
minexew

Non, il détecte les fichiers récemment ajoutés aux index. Je viens de vérifier.
sanmai

Voir question. Les fichiers non suivis ne sont pas des modifications . git addet git cleanà la rescousse
sanmai

4

J'ai créé des alias git pratiques pour répertorier les fichiers non et les fichiers intermédiaires:

git config --global alias.unstaged 'diff --name-only'
git config --global alias.staged 'diff --name-only --cached'

Ensuite, vous pouvez facilement faire des choses comme:

[[ -n "$(git unstaged)" ]] && echo unstaged files || echo NO unstaged files
[[ -n "$(git staged)" ]] && echo staged files || echo NO staged files

Vous pouvez le rendre plus lisible en créant un script quelque part sur votre PATHappelé git-has:

#!/bin/bash
[[ $(git "$@" | wc -c) -ne 0 ]]

Maintenant, les exemples ci-dessus peuvent être simplifiés pour:

git has unstaged && echo unstaged files || echo NO unstaged files
git has staged && echo staged files || echo NO staged files

Pour être complet, voici des alias similaires pour les fichiers non suivis et ignorés:

git config --global alias.untracked 'ls-files --exclude-standard --others'
git config --global alias.ignored 'ls-files --exclude-standard --others --ignored'

2

Avec python et le package GitPython:

import git
git.Repo(path).is_dirty(untracked_files=True)

Renvoie True si le référentiel n'est pas propre


Cela a évité certains des problèmes "d'horodatage" mentionnés dans d'autres commentaires
Jason

1
Notez que GitPython utilise également la CLI git. Si vous définissez, LOGLEVEL=DEBUGvous verrez toutes les commandes Popen qu'il utilise pour exécutergit diff
Jason

-3

Voici le meilleur moyen, le plus propre.

function git_dirty {
    text=$(git status)
    changed_text="Changes to be committed"
    untracked_files="Untracked files"

    dirty=false

    if [[ ${text} = *"$changed_text"* ]];then
        dirty=true
    fi

    if [[ ${text} = *"$untracked_files"* ]];then
        dirty=true
    fi

    echo $dirty
}

4
Non, ce n'est pas le meilleur. git statusest une commande «porcelaine». N'utilisez pas les commandes porcelain dans les scripts car elles peuvent changer entre les versions de git. Utilisez plutôt des commandes de plomberie.
spuder

3
Je pense que si vous l'avez mis à jour pour l'utiliser git status --porcelain(ce qui est destiné à cet effet - un format stable que vous pouvez analyser dans un script), peut-être aussi avec -z (séparé par zéro au lieu de la nouvelle ligne?), Vous pourriez faire quelque chose d'utile avec cette idée . @ codyc4321 voir stackoverflow.com/questions/6976473/… pour plus de détails
msouth
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.