2012-09-07 10 views
13

Internet está repleto de respuestas incorrectas y no ideales a esta pregunta. Esto es desafortunado porque pensarías que esto sería algo común que querrías hacer.Prueba de lo que está a punto de cometerse en un enganche previo a la confirmación

El problema: cuando se ejecuta un gancho pre-commit, es posible que el repositorio no esté limpio. Así que si ingenuamente ejecutas tus pruebas, no estarán en contra de lo que estás cometiendo, pero cualquier suciedad que se encuentre en tu árbol de trabajo.

Lo más obvio es hacer git stash --keep-index --include-untracked al comienzo de pre-commit y git pop en la salida. De esta manera, estás probando contra el índice (puro), que es lo que queremos.

Desafortunadamente, esto genera marcadores de conflictos de combinación si usa git add --patch, (especialmente si edita hunks), ya que el contenido de [email protected]{0} podría no coincidir con el árbol de trabajo después de la confirmación.

Otra solución común es clonar el repositorio y ejecutar las pruebas en uno nuevo temporal. Hay dos problemas con eso: uno es que aún no nos hemos comprometido, por lo que no podemos obtener fácilmente una copia del repositorio en el estado que estamos a punto de comprometer (estoy seguro de que hay una forma de hacerlo) , pero no estoy interesado porque :). En segundo lugar, mis pruebas pueden ser sensibles a la ubicación del directorio de trabajo actual. Por ejemplo, debido a la configuración del entorno local.

Entonces, ¿cómo puedo restaurar mi árbol de trabajo al estado en el que estaba antes del git stash --keep-index --include-untracked, sin introducir los marcadores de conflicto de fusión y sin modificar el HEAD post-commit?

+0

El guión pre-commit recibe los datos que se cometen como entrada. ¿Por qué necesitas mirar algo más? Tal vez lo que estás tratando de hacer se haga mejor en algo que no sea un gancho precompromiso. ¿Qué tipo de pruebas quieres hacer que requieren acceso al repositorio completo? –

+0

@WilliamPursell: ¿Qué quiere decir con "los datos se están comprometiendo?". El script precomprometido se ejecuta en mi árbol de trabajo (es decir, la base del repositorio de origen). El problema es que si realiza algunos cambios en el repositorio y solo monta algunos de ellos (por ejemplo, agrega algunos archivos pero no otros), entonces no estará probando el compromiso antes de que ocurra (lo que quiero hacer), estarías probando lo que tengas en tu directorio de trabajo. – pwaller

+0

El parche que está confirmando está disponible en stdin para el gancho precompromiso. ¿Qué estás probando si no es el parche que se está comprometiendo? El propósito del gancho precompromiso es verificar el parche. –

Respuesta

2

Si la clonación de todo el repositorio es demasiado costosa, quizás solo necesite una copia del directorio de trabajo. Hacer una copia sería más simple que intentar lidiar con conflictos. Por ejemplo:

#!/bin/sh -e 

trap 'rm -rf $TMPD' 0 
mkdir ${TMPD=$PWD/.tmpdir} 
git ls-tree -r HEAD | while read mod type sha name; do 
    if test "$type" = blob; then 
     mkdir -p $TMPD/$(dirname "$name") 
     git show $sha > $TMPD/"$name"; 
     chmod $mod $TMPD/"$name" 
    fi 
done 
cd $TMPD 
git diff --cached HEAD | patch 
# Run tests here 

Esto volcar el estado del árbol, ya que será después de la confirmación de $ DPMT, para que pueda ejecutar las pruebas allí. Debería obtener un directorio temporal de una manera más segura que aquí, pero para que el último diff funcione (o para simplificar el script y el cd anteriormente), debe ser un elemento secundario del directorio de trabajo.

-1

Finalmente he encontrado la solución que estaba buscando. Solo se comprueba el estado del índice antes de la confirmación, y deja el índice y el árbol de trabajo exactamente como estaba antes de la confirmación.

Si ve algún problema o una mejor manera, responda, ya sea como comentario o como respuesta propia.

Esto supone que nada más intentará esconder o modificar el repositorio de git o el árbol de trabajo mientras se está ejecutando. Esto viene sin garantía, puede ser incorrecto y arrojar su código al viento. UTILIZAR CON PRECAUCIÓN.

# pre-commit.sh 
REPO_PATH=$PWD 
git stash save -q --keep-index --include-untracked # ([email protected]{1}) 
git stash save -q         # ([email protected]{0}) 

# Our state at this point: 
# * clean worktree 
# * [email protected]{0} contains what is to be committed 
# * [email protected]{1} contains everything, including dirt 

# Now reintroduce the changes to be committed so that they can be tested 
git stash apply [email protected]{0} -q 

git_unstash() { 
    G="git --work-tree \"$REPO_PATH\" --git-dir \"$REPO_PATH/.git\"" 
    eval "$G" reset -q --hard    # Clean worktree again 
    eval "$G" stash pop -q [email protected]{1}  # Put worktree to original dirty state 
    eval "$G" reset -q [email protected]{0} .  # Restore index, ready for commit 
    eval "$G" stash drop -q [email protected]{0}  # Clean up final remaining stash 
} 
trap git_unstash EXIT 

... tests against what is being committed go here ... 
+0

Como se menciona en los comentarios en la siguiente publicación, esto no funcionará correctamente si se modifica una confirmación, o si no tiene un árbol de trabajo sucio. http: // codeinthehole.com/writing/tips-for-using-a-git-pre-commit-hook/ – pwaller

2

Si usted puede permitirse el lujo de utilizar un directorio temporal (. Es decir, hacer una copia completa de la caja actual) se puede utilizar un directorio temporal de este modo:

tmpdir=$(mktemp -d) # Or put it wherever you like 
git archive HEAD | tar -xf - -C "$tmpdir" 
git diff --staged | patch -p1 -d "$tmpdir" 
cd "$tmpdir" 
... 

Esto es básicamente William Pursell de solución, pero toma ventaja de git archive que hace que el código sea más simple, y espero que sea más rápido.

Alternativamente, por cd'ing primero:

cd somewhere 
git -C path/to/repo archive HEAD | tar -xf - 
git -C path/to/repo diff --staged | patch -p1 
... 

git -C requiere Git 1.8.5.

+0

Le he votado, pero no satisface "permanecer en $ PWD cuando se prueba", lo cual es necesario para algunos sistemas, como ir a las bibliotecas. – pwaller

+0

Además de las preocupaciones de pwaller, creo que esta solución no funciona si estás haciendo un 'git commit -a', porque tu script asume que solo estamos asignando archivos en etapas. – TanguyP

1

git write-tree es útil en pre-commit ganchos. Escribe un árbol en el repositorio del índice (este árbol se reutilizará cuando se complete la confirmación).

Una vez que el árbol se escribe en el repositorio, puede usar git archive | tar -x para escribir el árbol en un directorio temporal .

ej .:

#!/bin/bash 

TMPDIR=$(mktemp -d) 
TREE=$(git write-tree) 
git archive $TREE | tar -x -C $TMPDIR 

# Run tests in $TMPDIR 

RESULT=$? 
rm -rf "$TMPDIR" 
exit $RESULT 
Cuestiones relacionadas