¿Cómo puedo registrar una secuencia de comandos Bash si mi repositorio Git local tiene cambios?

176

Hay algunos scripts que no funcionan correctamente si verifican los cambios.

Lo intenté así:

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

¿Hay algún tipo de verificación booleana si ha habido cambios desde la última confirmación, o cómo puedo probar realmente si hay nuevos cambios en mi repositorio local?

Estoy haciendo todo esto para un script de creación de versión (que encontré en algún lugar aquí).

kmindi
fuente
3
¿Qué tiene de malo git status?
karlphillip
44
@karlphillip: Realiza un gran procesamiento que realmente no necesita.
Cascabel
1
@karlphillip es un comando de "porcelana", lo que significa: no es apropiado para su uso en el script porque la salida está diseñada para ser leída por humanos y puede cambiar (entre versiones o debido a la localización)
Andrew Spencer

Respuestas:

201

Lo que estás haciendo casi funcionará: deberías citar $CHANGEDen caso de que esté vacío y realizar -zpruebas de vacío, lo que significa que no hay cambios. Lo que quisiste decir fue:

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

Una cita de Git's GIT-VERSION-GEN:

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

Parece que estaba copiando eso, pero acaba de olvidar ese detalle de la cita.

Por supuesto, también podrías hacer esto:

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

O si solo le importa el caso "algo ha cambiado":

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

El uso --quiettiene el beneficio de que Git puede detener el procesamiento tan pronto como encuentre un único diferencial, por lo que es posible que no tenga que verificar todo su árbol de trabajo.

Cascabel
fuente
2
Hola, esa fue una de las mejores respuestas a una pregunta, solo me diste toda la información que necesitaba y otras también mi necesidad :). Solo necesitaré el último, "algo ha cambiado" :) y tenías razón, lo copié.
kmindi
9
El increíble script de finalización de bash parece usarse git diff --no-ext-diff --quiet --exit-codepara determinar el estado sucio.
mjs
3
@mjs: ¡Ese es un gran lugar para buscar cosas como esta! La --no-ext-diffopción es buena para la seguridad (en caso de que alguien haya configurado un controlador externo diff), aunque --exit-codeno debería ser necesario, ya que está implícito en --quiet.
Cascabel
44
Esto no funciona para mí, ya que no informa los archivos no rastreados
Cookie
1
git diff-indexinforma los cambios incluso si solo han cambiado los tiempos de modificación del archivo (y no su contenido). Si tiene touchun archivo, informará la modificación que git statusse reiniciará en ejecución . Usar git statuscomo a continuación es mejor.
Sampo
303

Utilizando git status:

cd /git/directory
if [[ `git status --porcelain` ]]; then
  # Changes
else
  # No changes
fi
ryanmoon
fuente
15
La mejor respuesta, una pena, las respuestas de los 'arquitectos de shell y git' son más altamente votadas.
jwg
44
Esto es genial, ya que tiene en cuenta los archivos no versionados y también con porcelana, debería ser más compatible con diferentes versiones de git.
Jayd
25
Para ignorar los archivos no rastreados: if [[ git status --porcelain --untracked-files=no]]; entonces
storm_m2138
44
Para verificar cambios locales -> if [[ $(git status --porcelain | wc -l) -gt 0 ]]; then echo CHANGED else echo NOT CHANGED locally fi
Carlos Saltos
3
Qué hace este código: Ejecute git status --porcelainver si la salida no está vacía. Si es así, eso significa que hay cambios presentes.
bhathiya-perera
18

Aunque la respuesta de Jefromi es buena, estoy publicando esto solo como referencia.

Desde el código fuente de Git hay un shscript que incluye lo siguiente.

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
}
Arrowmaster
fuente
Como referencia, y si leo esto correctamente, $1es una cadena que nombra una tarea que desea ejecutar y $2es una cadena que opcionalmente contiene un mensaje de error personalizado en caso de falla. Por ejemplo, llámalo comorequire_clean_work_tree deploy "Skipping deploy, clean up your work tree or run dev-push"
Umbrella
6

Tuve un problema similar, pero también tuve que buscar archivos agregados. Entonces hice lo siguiente:

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
Leon Waldman
fuente
5

git status es tu amigo

Cambie al directorio Git para git statustrabajar:

cd c:/path/to/.git

Establezca una variable para establecer el árbol de trabajo para que no obtenga el error 'Esta operación debe ejecutarse en un árbol de trabajo':

WORKTREE=c:/path/to/worktree

Capture la git statussalida en una variable Bash

Uso --porcelainque garantiza estar en un formato estándar y analizable:

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

Si -n (no es nulo), tenemos cambios.

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

else
  echo 'not changed';
fi
AndrewD
fuente
1
Esto tiene la ventaja de detectar también archivos no rastreados.
Luiz C.
2

Esto también funciona:

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

Esto funciona muy bien. También enumerará los archivos afectados:

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
fuente
1
si no quieres ver el diff git diff --no-ext-diff --quiet --exit-codetambién hace el trabajo.
Alex
1

Aquí hay un buen conjunto de funciones de script Bash que verifican si hay una diferencia, la imprime al usuario y le pregunta si desea confirmar los cambios antes de la implementación. Está construido para una aplicación Heroku y Python, pero necesita pocos cambios para cualquier otra aplicación.

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
}

Puede copiar desde Gists en: https://gist.github.com/sshadmand/f33afe7c9071bb725105

Sean
fuente
1

La pregunta del OP tiene más de 9 años. No sé lo que man git-statusdijo entonces, pero esto es lo que dice ahora:

--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.  

Esto sugiere que el --porcelainargumento es muy adecuado para probar el estado de un repositorio de cambios.

Wrt la pregunta del OP, "¿Hay algún tipo de verificación booleana si ha habido cambios desde la última confirmación, o cómo puedo realmente probar si hay nuevos cambios en mi repositorio local?"

No creo que bashtenga tipos de datos booleanos per se , pero esto puede estar lo suficientemente cerca:

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

Esto se puede volver a emitir como un if-then-elseformulario para una secuencia de comandos, o ejecutar como está desde la CLI mientras está en la carpeta git repo . De lo contrario, use la -Copción con una especificación de ruta al repositorio de interés:

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

Apéndice:

  1. Algunos aconsejan usar la -u, --untracked-fileopción para evitar informar el estado de los archivos que uno desea ignorar. Tenga en cuenta que esto tiene un efecto secundario desafortunado : los archivos que se agregan recientemente tampoco tienen estado. La opción es útil en algunas situaciones , pero considérela cuidadosamente antes de usarla.
Seamus
fuente
0

Así es como lo hago ...

CHANGES=`git status | grep "working directory clean"`
if [ ! CHANGES -eq "" ] then
    # do stuff here
else
    echo "You have uncommitted changes..."
fi
Jader Feijo
fuente
3
Me gusta usar el estado de git, pero es mejor usar --porcelain en los scripts y comparar el resultado con una cadena vacía para que no haya cambios, ya que se garantiza que no cambiará de forma incompatible entre las versiones.
Paul Chernoch
0
nano checker_git.sh

pegar esto

#!/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

correr sh checker_git.sh gitpath

Robert A
fuente
0

Basado en el comentario de @ storm_m2138 a la respuesta de @ RyanMoon ( enlace ) Estoy usando lo siguiente en 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
fuente