Comment puis-je archiver un script Bash si mon référentiel Git local a des modifications?

176

Certains scripts ne fonctionnent pas correctement s'ils vérifient les modifications.

Je l'ai essayé comme ceci:

VN=$(git describe --abbrev=7 HEAD 2>/dev/null)

git update-index -q --refresh
CHANGED=$(git diff-index --name-only HEAD --)
if [ ! -z $CHANGED ];
    then VN="$VN-mod"
fi

Existe-t-il une sorte de vérification booléenne s'il y a eu des changements depuis le dernier commit, ou comment puis-je vraiment tester s'il y a de nouveaux changements dans mon référentiel local?

Je fais tout cela pour un script de création de version (que j'ai trouvé quelque part ici).

kmindi
la source
3
Quel est le problème avec git status?
karlphillip
4
@karlphillip: Il effectue beaucoup de traitements dont vous n'avez pas vraiment besoin.
Cascabel
1
@karlphillip c'est une commande "porcelain", cela signifie: ne convient pas pour une utilisation dans un script car la sortie est conçue pour être lue par des humains et peut changer (entre les versions ou en raison de la localisation)
Andrew Spencer

Réponses:

201

Ce que vous faites fonctionnera presque: vous devriez citer $CHANGEDau cas où il serait vide et -ztester pour vide, ce qui signifie qu'aucun changement. Vous vouliez dire:

if [ -n "$CHANGED" ]; then
    VN="$VN-mod"
fi

Une citation de Git's GIT-VERSION-GEN:

git update-index -q --refresh
test -z "$(git diff-index --name-only HEAD --)" ||
VN="$VN-dirty"

On dirait que vous copiez cela, mais vous avez simplement oublié ce détail de la citation.

Bien sûr, vous pouvez également faire ceci:

if git diff-index --quiet HEAD --; then
    # No changes
else
    # Changes
fi

Ou si vous ne vous souciez que du cas "quelque chose a changé":

if ! git diff-index --quiet HEAD --; then
    VN="$VN-mod"
fi

L'utilisation --quieta l'avantage que Git peut arrêter le traitement dès qu'il rencontre un seul diff, il peut donc ne pas avoir à vérifier l'intégralité de votre arbre de travail.

Cascabel
la source
2
Salut, c'était l'une des meilleures réponses à une question, vous venez de me donner toutes les informations dont j'avais besoin et que d'autres ont aussi besoin de moi :). J'aurai juste besoin du dernier, "quelque chose a changé" :) et tu avais raison, je l'ai copié.
kmindi
9
L'incroyable script d'achèvement bash semble utiliser git diff --no-ext-diff --quiet --exit-codepour déterminer l'état sale.
mjs
3
@mjs: C'est en effet un bon endroit pour chercher des choses comme ça! L' --no-ext-diffoption est bonne pour la sécurité (au cas où quelqu'un a configuré un pilote de différence externe), bien que cela --exit-codene devrait pas être nécessaire, car cela est impliqué par --quiet.
Cascabel
4
Cela ne fonctionne pas pour moi car il ne signale pas les fichiers non suivis
Cookie
1
git diff-indexsignale les modifications même si seules les heures de modification des fichiers ont changé (et non leur contenu). Si vous avez touchun fichier, il signalera une modification qui git statussera réinitialisée. Utiliser git statuscomme ci-dessous est mieux.
Sampo
304

Utilisation git status:

cd /git/directory
if [[ `git status --porcelain` ]]; then
  # Changes
else
  # No changes
fi
Ryanmoon
la source
15
La meilleure réponse, dommage, les réponses des «architectes shell et git» sont plus votées.
jwg du
4
C'est génial car il prend en compte les fichiers non versionnés et utilise également la porcelaine, il devrait être plus compatible avec différentes versions de git.
Jayd
25
Pour ignorer les fichiers non suivis: if [[ git status --porcelain --untracked-files=no]]; puis
storm_m2138
4
Pour vérifier les changements locaux -> if [[ $(git status --porcelain | wc -l) -gt 0 ]]; then echo CHANGED else echo NOT CHANGED locally fi
Carlos Saltos
3
Ce que fait ce code: Exécuter pour git status --porcelainvoir si la sortie n'est pas vide. Si c'est le cas, cela signifie qu'il y a des changements.
bhathiya-perera
18

Bien que la réponse de Jefromi soit bonne, je publie ceci juste pour référence.

À partir du code source de Git, il existe un shscript 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
}
Maître des flèches
la source
Pour référence, et si j'ai bien lu, $1est une chaîne nommant une tâche que vous souhaitez exécuter et $2est une chaîne contenant éventuellement un message d'erreur personnalisé en cas d'échec. Par exemple, appelez-le commerequire_clean_work_tree deploy "Skipping deploy, clean up your work tree or run dev-push"
Umbrella
6

J'ai eu un problème similaire, mais j'ai également dû vérifier les fichiers ajoutés. J'ai donc fait ce qui suit:

cd /local/repo
RUN=0
git diff --no-ext-diff --quiet --exit-code || RUN=1
if [ $RUN = 0 ]; then
    RUN=`git ls-files --exclude-standard --others| wc -l`
fi

if [ $RUN = 0 ]; then
    exit 0
fi
Léon Waldman
la source
5

git status est votre ami

Accédez au répertoire Git pour git statustravailler:

cd c:/path/to/.git

Définissez une variable pour définir l'arborescence de travail afin que vous n'obteniez pas l'erreur `` Cette opération doit être exécutée dans un arbre de travail '':

WORKTREE=c:/path/to/worktree

Capturer la git statussortie dans une variable Bash

Utilisez --porcelainqui garantit d'être dans un format standard et analysable:

CHANGED=$(git --work-tree=${WORKTREE} status --porcelain)

Si -n (non nul), nous avons des changements.

if [ -n "${CHANGED}" ]; then
  echo 'changed';

else
  echo 'not changed';
fi
AndrewD
la source
1
Cela présente également l'avantage de détecter les fichiers non suivis.
Luiz C.
2

Cela fonctionne aussi:

if [ $(git status --porcelain | wc -l) -eq "0" ]; then
  echo "  🟢 Git repo is clean."
else
  echo "  🔴 Git repo dirty. Quit."
  exit 1
fi
blackjacx
la source
1

Cela fonctionne bien. Il listera également les fichiers concernés:

if git diff-index --name-status --exit-code HEAD;
then
    echo Git working copy is clean...;
else
    echo ;
    echo ERROR: Git working copy is dirty!;
    echo Commit your changes and try again.;
fi;
robertberrington
la source
1
si vous ne voulez pas voir le diff fait git diff --no-ext-diff --quiet --exit-codeégalement le travail.
Alex
1

Voici un bel ensemble de fonctions de script Bash qui vérifient s'il existe un diff, l'imprime à l'utilisateur et lui demande s'il souhaite valider les modifications avant le déploiement. Il est conçu pour une application Heroku et Python, mais il ne nécessite que peu de modifications pour toute autre application.

commit(){
    echo "Please enter a commit message..."
    read msg
    git add . --all
    git commit -am $msg
}

check_commit(){
    echo ========== CHECKING FOR CHANGES ========
    changes=$(git diff)
    if [ -n "$changes" ]; then
        echo ""
        echo "*** CHANGES FOUND ***"
        echo "$changes"
        echo ""
        echo "You have uncomitted changes."
        echo "Would you like to commit them (y/n)?"
        read n
        case $n in
            "y") commit;;
            "n") echo "Changes will not be included...";;
            *) echo "invalid option";;
        esac
    else
        echo "... No changes found"
    fi
}

deploy(){
    check_commit
    echo ========== DEPLOYING TO HEROKU ========
    git push heroku master
    heroku run python manage.py syncdb
}

Vous pouvez copier à partir de Gists sur: https://gist.github.com/sshadmand/f33afe7c9071bb725105

Sean
la source
1

La question du PO a plus de 9 ans maintenant. Je ne sais pas ce qui a été man git-statusdit alors, mais voici ce qu'il dit maintenant:

--porcelain[=<version>]  
Give the output in an easy-to-parse format for scripts. This is similar to the 
short output, but will remain stable across Git versions and regardless of user 
configuration. See below for details.  

The version parameter is used to specify the format version. This is optional and 
defaults to the original version v1 format.  

Cela suggère que l' --porcelainargument est bien adapté pour tester l'état d'un dépôt pour les modifications.

Répondez à la question de l'OP, "Y a-t-il une sorte de vérification booléenne s'il y a eu des changements depuis le dernier commit, ou comment puis-je vraiment tester s'il y a de nouveaux changements dans mon référentiel local?"

Je ne pense pas que cela bashait des types de données booléens en soi , mais cela peut être assez proche:

[ -z "`git status --porcelain`" ] && echo "NULL-NO DIFFS" || echo "DIFFS EXIST"

Cela peut être reconstitué en tant que if-then-elseformulaire pour un script, ou exécuté tel quel par l'interface de ligne de commande dans le dossier git repo . Sinon, utilisez l' -Coption avec une spécification de chemin vers le dépôt d'intérêt:

git -C ~/path/to/MyGitRepo status --porcelain 

Addenda:

  1. Certains conseillent d'utiliser l' -u, --untracked-fileoption pour éviter de signaler l'état des fichiers que l'on souhaite ignorer. Notez que cela a un effet secondaire malheureux : les fichiers nouvellement ajoutés ne sont pas non plus classés. L'option est utile dans certaines situations , mais considérez-la attentivement avant de l'utiliser.
Seamus
la source
0

Voici comment je fais ...

CHANGES=`git status | grep "working directory clean"`
if [ ! CHANGES -eq "" ] then
    # do stuff here
else
    echo "You have uncommitted changes..."
fi
Jader Feijo
la source
3
J'aime utiliser git status, mais il est préférable d'utiliser --porcelain dans les scripts et de comparer le résultat à une chaîne vide sans changement, car il est garanti de ne pas changer de manière incompatible entre les versions.
Paul Chernoch
0
nano checker_git.sh

collez ceci

#!/bin/bash

echo "First arg: $1"

cd $1

bob="Already up-to-date."
echo $bob

echo $(git pull) > s.txt
cat s.txt
if [ "$(cat s.txt)" == "$bob" ]
then
echo "up"
else
echo "not up"

fi
rm -rf st.txt

courir sh checker_git.sh gitpath

Robert A
la source
0

Basé sur le commentaire de @ storm_m2138 sur la réponse de @ RyanMoon ( lien ), j'utilise ce qui suit dans Powershell.

function hasChanges($dir="."){ $null -ne (iex "git -C $dir status --porcelain --untracked-files=no") }

gci -Directory | ?{ hasChanges $_ } | %{ Write-Host "$_ has changes" }
gci -Directory | ?{ hasChanges $_ } | %{ iex "git -C $_ add -u"; iex "git -C $_ commit -m"Somemthing" }
Dennis
la source