¿Cómo verificar si no hay nada que comprometerse en la sucursal actual?

172

El objetivo es obtener un estado inequívoco que pueda evaluarse en un comando de shell.

Lo intenté git statuspero siempre devuelve 0, incluso si hay elementos para confirmar.

git status
echo $?  #this is always 0

Tengo una idea pero creo que es una mala idea.

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

¿cualquier otra forma?


actualizar con la siguiente resolución, ver la publicación de Mark Longair

Intenté esto pero causa un problema.

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

Obtuve el siguiente error [: ??: binary operator expected

ahora, estoy mirando al hombre y pruebo el git diff.

=================== código para mi esperanza, y espero una mejor respuesta ======================

#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
fuente
44
En la sección actualizada, parece que en realidad no estás haciendo lo que eckes sugiere en su respuesta ; como él dice, debes poner comillas dobles alrededor del $(git status --porcelain). Además, si desea poner signos de exclamación en su mensaje, deberá usar comillas simples en lugar de comillas dobles, es decir, debería ser echo 'PLEASE COMMIT YOUR CHANGE FIRST!!!'en su lugar
Mark Longair
44
como dice Mark: ¡necesitas poner comillas dobles alrededor del$(git status --porcelain) , como te dije!
Eckes
1
Estas preguntas serían mucho más útiles si no incluyeran partes de las respuestas.
Oberlies
@ 9nix00 haga lo que le han dicho y edite y corrija el error en su script de shell anterior: ERROR: if [-z $ (algún comando)] FIX: if [-z "$ (algún comando)"]
MarcH

Respuestas:

232

Una alternativa para probar si la salida de git status --porcelainestá vacía es probar cada condición que le interesa por separado. Uno no siempre se preocupa, por ejemplo, si hay archivos sin seguimiento en la salida de git status.

Por ejemplo, para ver si hay cambios locales no organizados, puede consultar el código de retorno de:

git diff --exit-code

Para verificar si hay cambios que se realizan pero no se confirman, puede usar el código de retorno de:

git diff --cached --exit-code

Finalmente, si desea saber si hay archivos no rastreados en su árbol de trabajo que no se ignoran, puede probar si la salida del siguiente comando está vacía:

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

Actualización: Usted pregunta a continuación si puede cambiar ese comando para excluir los directorios en la salida. Puede excluir directorios vacíos agregando --no-empty-directory, pero para excluir todos los directorios en esa salida, creo que tendrá que filtrar la salida, como con:

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

El -vto egrepsignifica solo generar líneas de salida que no coinciden con el patrón, y el patrón coincide con cualquier línea que termine con a /.

Mark Longair
fuente
Aprendí estos consejos y tengo un problema. es decir, use git ls-files --other --exclude-standard --directory obtenga la lista incluye directorios. ¿hay alguna forma de excluir estos directorios?
9nix00
Sí, esto es lo que quiero. y actualizo mi publicación para el nuevo código de script. Creo que su sugerencia es más estricta lógica aunque más código jajaja ... y espero que aparezcan mejores respuestas.
9nix00
3
@albfan: está en la página de manual de git-diff : "Haga que el programa salga con códigos similares a diff (1). Es decir, sale con 1 si hubo diferencias y 0 significa que no hay diferencias".
Mark Longair
Solo para señalar, ha estado allí al menos desde 2007 13da0fc0 , realmente útil para los scripts de shell y totalmente compatible con versiones anteriores de git
albfan
10
--quiet(lo que implica --exit-code) también silencia la salida, para aquellos que solo quieren el código de salida.
phs
113

El valor de retorno de git statussimplemente le indica el código de salida de git status, no si hay modificaciones para confirmar.

Si desea una versión más legible por computadora de la git statussalida, intente

git status --porcelain

Consulte la descripción de git statuspara obtener más información al respecto.

Uso de muestra (el script simplemente prueba si git status --porcelainda algún resultado, no se necesita análisis):

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

Tenga en cuenta que debe citar la cadena para probar, es decir, la salida de git status --porcelain. Para obtener más sugerencias sobre construcciones de prueba, consulte la Guía avanzada de secuencias de comandos Bash ( comparación de cadenas de sección ).

Eckes
fuente
hola, das una buena sugerencia, lo probé, pero en el script, causa algún problema, lo mejoré, si usamos esto si [-z $ (estado de git - porcelana)]; obtendrá algún error, [: ??: el operador binario esperaba encontrar el manual y usarlo si [-z $ (git status --short)]; esto puede funcionar, gracias!
9nix00
Lo siento, todavía hay un problema. cuando el commit está limpio. use porcelana y short ambos bien. pero cuando commit no está limpio. Causará un error. [: ??: operador binario esperado. Creo que tal vez deberíamos intentar usar base64 para codificarlo. ¡Déjame intentarlo! base64 descarga de comando herramientas .... lol
9nix00
causa problemas cuando el commit no está limpio.
9nix00
1
Gran solución Para mayor robustez, puede agregar || echo noa la sustitución de comandos para que el espacio de trabajo no se informe erróneamente limpio si git statusfalla fundamentalmente. Además, su código es (encomiable) compatible con POSIX, pero como se vincula a una guía de bash, permítame agregar que si usa bash en [[ ... ]]lugar de compatible con POSIX [ ... ], no necesita citar dos veces la sustitución del comando (aunque no hace daño): [[ -z $(git status --porcelain) ]].
mklement0
@eckes Comencé un nuevo repositorio hace unos días, agregué un commit, traté de escribir un gancho previo al commit y verificar lo que se debe confirmar y lo que usted dice no funcionó en mi caso.
alinsoar
33

Si eres como yo, quieres saber si hay:

1) cambios en archivos existentes 2) archivos recién agregados 3) archivos eliminados

y específicamente no quiero saber acerca de 4) archivos sin seguimiento.

Esto debería hacerlo:

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

Aquí está mi código bash para salir del script si el repositorio está limpio. Utiliza la versión corta de la opción de archivos sin seguimiento:

[[ -z $(git status -uno --porcelain) ]] && echo "this branch is clean, no need to push..." && kill -SIGINT $$;
humor
fuente
44
+1 para —untracked-files=no; Creo que tu prueba se puede simplificar [[ -z $(git status --untracked-files=no --porcelain) ]]. git statusno debe escribir en stderr, a menos que algo va mal fundamental - y entonces usted no quiere ver que la producción. (Si desea un comportamiento más robusto en ese caso, agregue || echo nola sustitución de comandos para que la prueba de limpieza aún falle). Comparaciones de cadenas / el -zoperador puede manejar cadenas de varias líneas, sin necesidad de hacerlo tail.
mklement0
2
gracias @ mklement0, incluso un poco más corto:[[ -z $(git status -u no --porcelain) ]]
moodboom
1
corrección: ¡mi versión más corta en realidad solo verifica el estado en el archivo llamado "no"! Bzzt. Debería ser: [[ -z $(git status -uno --porcelain) ]]
moodboom
2
Agradezco el seguimiento; eso es un error sutil: la lección es que las opciones cortas con argumentos opcionales deben tener el argumento agregado directamente , sin espacios en blanco en el medio. ¿Qué tal incorporar la versión corta corregida directamente en su respuesta?
mklement0
9

Es posible combinar git status --porcelaincon un simple greppara realizar la prueba.

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

A veces lo uso como una frase simple:

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

Agregue -qsa su comando grep para hacerlo silencioso.

Steve Prentice
fuente
2
+1 para elegancia; ligera advertencia: si git statusfalla fatalmente (p. ej., un repositorio corrupto), su prueba informará por error de un espacio de trabajo limpio . Una opción es usar git status --porcelain 2>&1, pero eso 'comería' el mensaje de error si usara grep con -q. (Que trata de que iba a perder la elegancia: (git status --porcelain || echo err) | grep -q .)
mklement0
alternativamente, uno puede escribir:test -z "$(git status --porcelain)" || git pull origin master
VasiliNovikov
5

Desde el código fuente de git hay un script sh 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
}

Este fragmento muestra cómo es posible usarlo git diff-filesy git diff-indexdescubrir si hay algún cambio en los archivos conocidos previamente. Sin embargo, no le permite averiguar si se ha agregado un nuevo archivo desconocido al árbol de trabajo.

Arrowmaster
fuente
Esto funciona bien, excepto para los archivos nuevos. Podemos agregar esto. Si ! git ls-files --other --exclude-standard --directory | grep -c -v '/ $' luego salga 0 else echo "por favor confirme su nuevo archivo, si no desea agregarlo, agréguelo en el archivo git-ignore". salida 1 fi
9nix00
Simplemente if [ -n "$(git ls-files --others --exclude-standard)" ]sin ninguna tubería adicional o greping debería ser suficiente para detectar archivos no rastreados.
Arrowmaster
5

haría una prueba sobre esto:

git diff --quiet --cached

o esto para ser explícito:

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

dónde:

- código de salida

Haga que el programa salga con códigos similares a diff (1). Es decir, sale con 1 si hubo diferencias y 0 significa que no hay diferencias.

--tranquilo

Deshabilita todas las salidas del programa. Implica --código de salida

archf
fuente
3

Llego un poco tarde en la discusión, pero si es solo que necesita tener un código de salida de 0 si git status --porcelainno devuelve nada y! = 0 más, intente esto:

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

Esto hará que el número de líneas sea el código de salida, a riesgo de problemas cuando haya más de 255 líneas. Entonces

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

tendrá en cuenta eso;)

Skeeve
fuente
1
Esto básicamente será indefinido si hay más de 255 líneas de salida.
Tripleee
Bien visto, gracias!
Skeeve
2

Estoy usando esto en un script para tener:

  • 0 cuando todo está limpio
  • 1 cuando hay un archivo diff o sin seguimiento

    [-z "$ (estado de git - porcelana)"]

más asqueroso
fuente
usar if ! git diff --quiet; thenes más limpio y más eficiente (creo). En otras palabras, use el código de salida, no el stdout.
Alexander Mills el
1
@AlexanderMills se git diff --quietcomporta de manera diferente git status --porcelaina los cambios en caché.
Martin von Wittich
0

No es bonito, pero funciona:

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

No estoy seguro de si el mensaje depende de la configuración regional, así que quizás ponga un LANG=Cfrente.

Tilman Vogel
fuente