el gancho de pre-recepción no puede leer el file comprometido para enviarlo al maestro remoto

Intenté lo siguiente en el file de pre-recepción en mi server pero mi precompromiso en mi repository git local funciona sin problemas

#!/bin/bash git rev-parse -q --verify HEAD && sed -n -e '/^<<\+ /q1' -n -e '/^>>\+ /q1' $(git diff --name-only HEAD^ HEAD $(git write-tree)) || { echo "Git conflict (<<<<) or (>>>>) exists in one of the committed files."; exit 1; } 

pero obtenga el error como remote: sed: can't read /App/abc/TestFile.java:No such file or directory

por favor ayúdame a resolverlo?

Esto es un poco desorderado

Tomemos esta parte primero, de su gancho precompromiso:

 git diff --name-only HEAD^ HEAD $(git write-tree) 

El git write-tree escritura interno de git write-tree escribe el índice en un tree y devuelve su valor hash. Por el bien del ejemplo, digamos que es 01234567 .

A continuación, ejecuta git diff con tres arguments commit-or-tree-ish (los tres arguments se pueden resolver con un identificador de tree, que es lo que a Git le interesa):

 HEAD^ HEAD 01234567 

Esto invoca un bit de comportamiento no documentado en git diff: produce un "diff combinado". Las inputs al diff combinado se consideran varios padres (todos less el primer argumento) y un hijo (el primer argumento), por lo que trata el tree que acaba de escribir, y el compromiso HEAD en el repository, ya que los dos padres se comprometen , con HEAD^ como el niño se compromete.

La documentation de git diff señala que un diff combinado "enumera solo los files que fueron modificados de todos los padres". En este caso, una vez más, los dos "padres" son el tree de la nueva propuesta propuesta (de git write-tree ) y la confirmación HEAD (la que se encuentra actualmente en la punta de la twig actual). Donde ambos difieren de HEAD^ (el primer padre de la punta de la twig actual), git le mostrará un diff. ¡Esto no es lo que quieres! (Dado que también especificó --name-only , git le mostrará solo los nombres de los files, no la diferencia real).

A continuación, toma esos nombres y busca en esos files los "marcadores de conflicto" de git (las marcas <<< y >>> en regiones conflictivas). Esta parte no es tan incorrecta (pero aún está ligeramente rota), pero en este punto las cosas ya están mal porque es posible que esté mirando los files incorrectos.

Considere, por ejemplo, el caso donde commit HEAD^ carece del file f2 , commit HEAD agrega el file f2 , y el índice actual modifica el file f3 pero tiene un conflicto de git en él:

 $ mkdir /tmp/repo; cd /tmp/repo; git init Initialized empty Git repository in /tmp/repo/.git/ $ echo ordinary file > f1; git add f1 $ echo another ordinary file > f3 $ git add f1 f3; git commit -m initial [master (root-commit) f181096] initial 2 files changed, 2 insertions(+) create mode 100644 f1 create mode 100644 f3 $ echo new file f2 > f2; git add f2 $ git commit -m 'add f2' [master c06f8d1] add f2 1 file changed, 1 insertion(+) create mode 100644 f2 $ (echo '<<< conflict'; echo '==='; echo '>>> end conflict') > f3 $ git add f3 # but we never resolved our (fake) conflict $ git diff --name-only HEAD^ HEAD $(git write-tree) f2 

Ahí está el problema: el diff combinado no miró a f3 ya que no se modifica tanto en "padres" como en "niño" (por supuesto, estas relaciones "parentales" / "niño" son sin sentido de todos modos). Sin --name-only vemos la salida de diff combinada:

 $ git diff HEAD^ HEAD $(git write-tree) diff --cc f2 index 9d57e62,9d57e62..0000000 deleted file mode 100644,100644 --- a/f2 +++ /dev/null @@@ -1,1 -1,1 +1,0 @@@ --new file f2 

Si desea verificar si el tree de su nueva propuesta tiene algunos files con marcadores de conflicto, debe examinar los "blobs" propuestos, en lugar del tree de trabajo actual. (Esto se debe a que puede git add un file, luego modificarlo más, o git add -p para seleccionar interactivamente las partes que se agregarán y las partes para diferir la adición. Por lo tanto, el contenido del índice puede no coincidir con el directory de trabajo). una cantidad de forms de hacer esto; vea esta pregunta y su respuesta para un método, y debajo (usando git show con una revisión y ruta) para otro. El código que tienes ahora funcionará para algunos casos, pero definitivamente no para todos.


Con eso fuera del path, veo que Ikke ya ha respondido el otro problema , que es que un repository vacío, el objective habitual para git push operaciones de git push , y el lugar donde se ejecutaría un gancho de pre-recepción, no tiene tree de trabajo. , por lo que no puede ver los files en ese tree de trabajo. Los ganchos pre-recibidos son generalmente más difíciles de escribir ya que debe manejar muchos casos:

  • commits múltiples
  • references que no son twigs (tags)
  • objects que no son commits (tags anotadas)
  • creaciones y eliminaciones de twigs, así como actualizaciones

Cuando se propone actualizar una twig (una reference de la forma refs/heads/ name ), el gancho de pre-recepción obtiene su SHA-1 actual y una nueva SHA-1 propuesta. Luego puede usar git rev-list para encontrar la secuencia de objects que estará activada (o que ya no estará activada) en la twig si permite la actualización. Para cada object, si se trata de una confirmación, examinará el tree adjunto a esa confirmación, para ver si todos los blobs (files) en ese tree pasan la inspección.

Tenga en count que pre-receive ganchos de pre-receive y update son muy diferentes de otros ganchos "pre" de git: en ambos casos, los nuevos compromisos y / o tags anotadas propuestas ya están en el repository (aunque pueden ser eliminados nuevamente si hook los rechaza), y generalmente debería referirse a estos objects git propuestos por ID de object (SHA-1). (Está bien caminar por el tree de commits, de hecho, debes hacer esto en muchos casos). El punto aquí es que lo que es correcto para un enganche pre-commit está casi garantizado que es incorrecto para un hook previo a la recepción, y viceversa versa.

Un bosquejo aproximado de este process podría ser:

 NULL_SHA1=0000000000000000000000000000000000000000 check_revs() { local range branch rev rtype path range=$1 branch=$2 git rev-list $range | while read rev; do rtype=$(git cat-file -t $rev) case $rtype in commit) ;; *) continue;; # skip annotated tags esac git diff --name-only ${rev}^ $rev | while read path; do if git show ${rev}:$path | grep forbidden-item; then echo "error: branch ${branch}: ${rev}:$path contains forbidden-item" 1>&2 exit 1 fi done done } check_branch() { local old new branch old=$1 new=$2 branch=$3 if [ $old = $NULL_SHA1 -o $new = $NULL_SHA1 ]; then # branch will be created or deleted, not updated # do whatever is appropriate here else # branch will be updated, if we allow it check_revs $old..$new $branch fi } while read oldsha newsha fullref; do case "$fullref" in refs/heads/*) check_branch $oldsha $newsha ${fullref#refs/heads/};; # add cases for refs/tags/* if desinetworking, etc *) ;; esac exit 0 # if we got this far it must be OK to do it 

(Tenga en count que esto no se ha probado por completo. Supongo que tiene un error en el caso del "file eliminado", donde no hay nada que git show en la nueva revisión. Además, no es necesariamente una buena idea verificar <<< y el pero, ¿qué pasa si hay un file de text que muestra cómo se ven los conflictos de git? Puede elegir el tipo de inspección que se debe hacer según el nombre del file, pero incluso algunos files pueden contener legítimamente lo que parece, pero no son en realidad , git marcadores de conflicto. Si decide hacer esto, asegúrese de permitir algún path a su alnetworkingedor si hay un caso en el que debería permitirse).

El problema es que sed trata de leer el file del sistema de files, pero los repositorys desnudos no tienen un tree de trabajo.

git write-tree crea un nuevo object de tree, no revisa los files, por lo que no es adecuado para lo que desea.

También solo está verificando las diferencias del último compromiso, mientras que alguien puede presionar muchos commits. El gancho de pre-recepción proporciona información sobre los commits que se están presionando en stdin (consulte la página de manual para el formatting).

Debe iterar cada línea y usarla para crear una diferencia que pueda alimentar a sed

git diff "$old_commit"..."$latest_commit" donde old_commit y latest_commit son las inputs pnetworkingeterminadas para pre-recibir. Muestra los diffs entre los commits más antiguos y los más recientes.