Determinar si el directorio de trabajo de Git está limpio de un script

84

Tengo un script que se ejecuta rsynccon un directorio de trabajo de Git como destino. Quiero que el script tenga un comportamiento diferente dependiendo de si el directorio de trabajo está limpio (sin cambios para confirmar), o no. Por ejemplo, si la salida de git statuses la siguiente, quiero que salga el script:

git status
Already up-to-date.
# On branch master
nothing to commit (working directory clean)
Everything up-to-date

Si el directorio no está limpio, me gustaría ejecutar algunos comandos más.

¿Cómo puedo verificar la salida como la anterior en un script de shell?

brentwpeterson
fuente
¿Verificaría un estado del último comando ayudar aquí? ($?)
UVV
¿Podría dar más detalles por favor? ¿Cuál es la idea principal para tu guión?
tachomi
@tachomi agregué el contexto en la edición
brentwpeterson
simplemente podría suponer que no está limpio y hacer git reset --hard origin/brancheso si eso es lo que está buscando ... como si intentara limpiar después de compilar algo, etc.
SnakeDoc
1
@SnakeDoc Podría, pero supongo que el caso inverso sería más común, es decir, salir si el directorio de trabajo está sucio para evitar alterar los cambios locales. Considerar ambos casos haría que la pregunta sea más útil para futuros lectores.
Thomas Nyman

Respuestas:

135

Analizar la salida de git statuses una mala idea porque la salida está destinada a ser legible por humanos, no legible por máquina. No hay garantía de que la salida sea la misma en futuras versiones de Git o en entornos con configuraciones diferentes.

El comentario de UVV está en el camino correcto, pero desafortunadamente el código de retorno de git statusno cambia cuando hay cambios no confirmados. Sin embargo, proporciona la --porcelainopción, que hace que la salida git status --porcelainse formatee en un formato fácil de analizar para los scripts, y se mantendrá estable en todas las versiones de Git e independientemente de la configuración del usuario.

Podemos usar la salida vacía de git status --porcelaincomo un indicador de que no hay cambios que confirmar:

if [ -z "$(git status --porcelain)" ]; then 
  # Working directory clean
else 
  # Uncommitted changes
fi

Si no nos interesan los archivos no rastreados en el directorio de trabajo, podemos usar la --untracked-files=noopción para ignorarlos:

if [ -z "$(git status --untracked-files=no --porcelain)" ]; then 
  # Working directory clean excluding untracked files
else 
  # Uncommitted changes in tracked files
fi

Para hacer esto más robusto contra las condiciones que realmente causan git statusfallas sin salida stdout, podemos refinar la verificación para:

if output=$(git status --porcelain) && [ -z "$output" ]; then
  # Working directory clean
else 
  # Uncommitted changes
fi

También vale la pena señalar que, aunque git statusno proporciona un código de salida significativo cuando el directorio de trabajo no está limpio, git diffproporciona la --exit-codeopción, que hace que se comporte de manera similar a la utilidad diff , es decir, salir con el estado 1cuando hubo diferencias y 0cuando no se encontraron ninguno.

Con esto, podemos verificar los cambios no organizados con:

git diff --exit-code

y escenificó, pero no cometió cambios con:

git diff --cached --exit-code

Aunque git diffpuede informar sobre archivos no rastreados en submódulos a través de argumentos apropiados --ignore-submodules, desafortunadamente parece que no hay forma de que informe sobre archivos no rastreados en el directorio de trabajo real. Si los archivos no rastreados en el directorio de trabajo son relevantes, git status --porcelaines probablemente la mejor opción.

Thomas Nyman
fuente
44
ughhh git status --porcelainsaldrá con el código 0 incluso si hay cambios no organizados para commit y archivos no rastreados.
Alexander Mills
Estaba interesado en determinar con anticipación si git stashharía algo (no genera un código de retorno útil). Tuve que agregar --ignore-submodulesya que de git statuslo contrario indicaría cambios en el submódulo que git stashignora.
Devin Lane,
1
@AlexanderMills: Observé lo mismo. Pero luego comprobé lo que if [ -zestaba haciendo. Esto -zsignifica que si la siguiente cadena está vacía, el if se evalúa como true. En otras palabras, si esto no git status --porcelainda como resultado una cadena, el repositorio está limpio. Si no, enumera los archivos modificados / agregados / eliminados y ya no es una cadena vacía. El ifentonces evalúa a false.
Adeynack
19

Utilizar:

git diff-index --quiet HEAD

El código de retorno refleja el estado del directorio de trabajo (0 = limpio, 1 = sucio). Los archivos sin seguimiento se ignoran.

André van Herk
fuente
66
Devuelve 0 cuando hay archivos sin seguimiento en el directorio actual.
Adam Parkin
2
Si los archivos se han tocado / sobrescrito pero son idénticos al índice, primero debe ejecutarlos git update-index --refreshantes git diff-index HEAD. Más información: stackoverflow.com/q/34807971/1407170
sffc
@ AdamParkin Acabo de agregar todos los archivos git add .antes de emitirlo. Por lo general, es la forma de usarlo en un script
ceztko
Esto es genial. Tenga en cuenta que un código de retorno / salida distinto de cero también se interpreta como un 'error', que si está en un script con set -e, entonces su script se cerrará si está 'sucio'. Esto se puede evitar haciendo set +eantes de la llamada gity agregando set -enuevamente después de haber evaluado $?.
orion elenzil
1

Extensión menor a la excelente respuesta de André .

Esta es una forma de evaluar los resultados y también evitar un escollo si se encuentra en un script que emitió previamente set -e .

Los archivos sin seguimiento se ignoran.

set +e
git diff-index --quiet HEAD

if [ $? == 1 ] ; then
  set -e
  GIT_MODS="dirty"
else
  set -e
  GIT_MODS="clean"
fi
orion elenzil
fuente