Error de avance rápido al presionar a Git

Recibo un error de non-fast-forward al tratar de presionar a un repository de git de WordPress, pero tirar como dice el post de error me da un post de que todo está actualizado. Esto es justo después de un pull, merge y commit. Aquí está mi logging: http://pastebin.com/6M4qLqjG

En resumen, bash presionar:

 ajh$ git push staging master 

y recibo este error, diciéndome que tire primero:

 To git@git.wpengine.com:staging/gordo.git ! [rejected] master -> master (non-fast-forward) error: failed to push some refs to 'git@git.wpengine.com:staging/gordo.git' hint: Updates were rejected because a pushed branch tip is behind its remote hint: counterpart. Check out this branch and integrate the remote changes hint: (eg 'git pull ...') before pushing again. hint: See the 'Note about fast-forwards' in 'git push --help' for details. 

Pero si trato de tirar, dirá que está actualizado:

 ajh$ git pull staging master From git.wpengine.com:staging/gordo * branch master -> FETCH_HEAD Already up-to-date. 

Y esto funcionará, no sé lo que está pasando. ¿Alguna idea de lo que puedo hacer para que mi repo y local estén alineados? Nunca antes había visto este error de non-fast-forward , no estoy seguro de qué hacer con esto. También me está diciendo que revise el maestro, pero acabo de retirarme y fusionarme con el maestro, así que no creo que deba volver a intentarlo.

========= editar con corrección ============================================================================================================================================================ Todavía podría usar una explicación sobre qué está pasando exactamente. Puedo duplicar estos pasos pero realmente no entiendo por qué funcionan y el push / pull regular no.

http://pastebin.com/u9DAiU5P

Básicamente creé una nueva twig, desprotegida desde el maestro, y me envió rápidamente. Algo extraño sucede donde tengo que presionar para hacer la git push staging staging , las twigs en esto son confusas

Miré su segundo enlace pastebin y veo mucha confusión (lo cual no es sorprendente ya que la terminología de git parece casi deliberadamente confusa).

Creo que la mejor apuesta aquí es dar un paso atrás y echar un vistazo a la terminología de git primero, con algunos ejemplos del pastebin.

Comencemos con "remoto".

¿Qué es un control remoto?

Un control remoto es solo un nombre, como origin o staging o upstream , que proporciona a git (y a usted) un nombre corto para la URL completa de un repository homólogo de git. Pero git aprovecha este nombre corto para darte "twigs de rastreo remoto", que no podemos describir todavía. Primero, debemos hablar de "twigs".

¿Qué es una twig?

Aquí hay un poco del pastebin:

 1. The-Dorfchester:gordo.dev ajh$ git checkout -b trouble 2. M .idea/workspace.xml 3. M wp-content/themes/goTheme2015/blog-loadMore.php 4. M wp-content/themes/goTheme2015/home.php 5. M wp-content/themes/goTheme2015/stylesheets/layout.css 6. M wp-content/themes/goTheme2015/stylesheets/layout.scss 7. Switched to a new branch 'trouble' 8. The-Dorfchester:gordo.dev ajh$ git push trouble 9. warning: push.default is unset; ... 

En la línea 1, ha creado un nuevo nombre de sucursal, trouble .

En git, la palabra "twig" tiene al less dos significados distintos. 1 Hay nombres de twigs, como trouble , y luego hay secuencias en el gráfico de confirmaciones almacenadas en el repository, que forman twigs reales. Para distinguirlos de los nombres de las sucursales, llamémoslos "fragments de charts de compromiso". Dado que estos son términos difíciles de manejar, observemos que el gráfico de compromiso es, técnicamente hablando, un gráfico acíclico dirigido o "DAG". Una parte de este gráfico es, por lo tanto, un "DAGlet". 2

Los DAGlets importan porque esta queja de git:

  ! [rejected] master -> master (non-fast-forward) 

sucede cuando le pides a git que haga un empujón que "olvidará" parte de un DAG, es decir, "perderá un DAGlet". Volveremos sobre esto más tarde.

Sucursales vs commits

Voy a suponer que tienes una noción razonable de "commit", que en git guarda una instantánea completa de tu trabajo, más precisamente, una copy de lo que está en el "índice" o "área de ensayo", junto con un post de confirmación, su nombre y correo electrónico, y el momento en que realiza la confirmación. Sin embargo, tienen una cosa más que es muy importante, que veremos en la próxima sección.

Echemos un vistazo a las líneas 2-7 ahora. Cuando hiciste esto, git checkout -b trouble , git notó que estaba reteniendo un grupo de files modificados y no comprometidos como modified-and-not-committed.

Sé que estás tratando de hacer un git push , y git push empuja commits , no files. Por lo tanto, si tiene files modificados pero no confirmados, esos cambios no confirmados no se pueden enviar.

(No muestra un git status allí, pero el git status es realmente el mejor command para usar para ver qué está pasando en términos de files modificados y no comprometidos. Le dice en qué twig se encuentra ahora (lo que haría ser un trouble aquí, y si su twig está "rastreando" otra twig, qué tan adelante y / o detrás de su twig. Sin embargo, su git checkout -b efectivamente ejecutó el git status para nosotros.)

Lo que esto significa es que en algún momento, probablemente deberías ejecutar git commit para hacer una nueva confirmación. Cuando haces una nueva confirmación, ¿a dónde va?

Los compromisos se sumn a una twig

Siempre que realice una nueva confirmación, su nueva confirmación tiene esa "otra cosa" más arriba: tiene una identificación principal . Para la mayoría de las confirmaciones nuevas, la ID principal es "la confirmación actual". Esto vincula el nuevo compromiso con uno anterior y podemos dibujarlo como una pequeña flecha:

 A <- B <- C <- D 

Aquí, comenzamos con la confirmación más reciente (y actual), a la que llamo D D almacena el ID de su padre C confirma; C almacena el ID de su padre B ; y B almacena la identificación de su padre A

Acabamos de dibujar un DAGlet: elegimos un compromiso inicial (en este caso, D ), y ese compromiso nos da una cadena de compromisos más antiguos. Si hacemos un nuevo compromiso, llamémoslo E -it señalará de nuevo a D :

 A <- B <- C <- D <- E 

y tenemos un DAGlet más largo.

¿Dónde entra el nombre de la twig?

Un nombre de sucursal nos permite encontrar un compromiso específico. Eso es casi todo un nombre de twig: es un puntero a una confirmación. Si estás en Branch master y realizas una nueva confirmación, git comienza por encontrar el commit actual: tiene un gran "SHA-1 SHA-1" verdadero nombre horrible, y algunas veces verás una versión abreviada de esto como b66c9f0 -y hacer una nueva confirmación desde el área de preparación, su nombre y correo electrónico, su post de confirmación y esta ID principal. Entonces, esta es la parte engañosa, git escribe el nuevo compromiso SHA-1 en el nombre de la sucursal.

De esta manera, la twig misma ha crecido y el nombre de la twig apunta a la nueva "confirmación de punta", al final de la twig. Si solía terminar en D , ahora tiene D <- E con el master nombre apuntando a E lugar de D Commit E ahora es "la punta más", y su twig (nombre) apunta a E , con toda la cadena comenzando en E formando la twig (DAGlet). Aquí está el antes y después:

 [before] A <- B <- C <- D <-- master [after] A <- B <- C <- D <- E <-- master 

¿Por qué todo esto importa?

Cuando vas a git push algunos commit (s), tienes tu git hand los commits con el otro git -el git que escucha el URL almacenado bajo el control remoto- y luego le pides a ese git que establezca su branch al tip-most cometer que estás presionando.

Supongamos, sin embargo, que le pides a tu idiota que empuje a tu master (cometer E ) a su lado y que su punto master también sea E Pero, por cualquier razón, su master existe ahora, pero apunta a un compromiso diferente:

 A <- B <- C <- D <- F <-- master (in their repo) \ E <-- (your proposed replacement) 

Si su git establece su master en su repository para apuntar a comprometer E , conseguirá que usen el mismo DAGlet que tiene: E apuntando a D , apuntando a C , y así sucesivamente. Commit F , el compromiso que tienen, que no se perderán.

Esto es lo que significa un non-fast-forward . Significa que tienen su nombre de twig apuntando a una confirmación que no tienes en cualquier DAGlet que estés presionando. Ese compromiso apunta a algunos padres, que apuntan a más padres, y así sucesivamente. Eventualmente, o incluso de inmediato, esos commits históricos se unen, pero hay al less un commit que tienen y que no se perderán si hacen que su nombre de sucursal apunte a (su copy de) su DAGlet.

Revisión hasta el momento:

  • git push : necesita un control remoto, es decir, un nombre corto como origin o staging que proporciona la URL para empujar. Esa es la primera palabra después del push . Cualquier palabra adicional es "refspecs", que no hemos definido, pero por ahora digamos que están formadas por nombres de twig (que es mayormente cierto). Le das a git un nombre de twig como master o trouble e intenta empujar las confirmaciones que tienes bajo ese nombre, al control remoto, usando otro nombre de sucursal en el control remoto. ( ¿Qué nombre de sucursal, en el control remoto? Bien, veremos algo más sobre esto en un momento).

  • El DAGlet que empuja debería simplemente extender su twig, agregando nuevas confirmaciones. Técnicamente hablando, la comisión a la que les pides que establezcan su twig, debería ser la comisión de confirmación que ya tienen (esta es una situación de "no empuje real"), o debería apuntar a esa confirmación. Puede agregar un commit o muchos, pero en algún lugar de la secuencia, uno de sus nuevos commits tiene que apuntar a su commit tip existente.

  • O bien, puede insert un nombre de twig que no tienen. Si crea el nombre en el control remoto, no habrá ningún compromiso para que pierda.

Barra lateral: push.default

Consideremos la línea 9, la warning: push.default is unset . Esta advertencia se extiende a lo largo de la línea 28. Esta advertencia aparece cada vez que ejecuta git push con un control remoto, pero sin arguments de refspec adicionales. Para hacer que push.default se push.default , recomiendo configurar push.default , ya sea simple o upstream .

Puede hacer esto una vez por repository, o configurarlo en su configuration global personal (donde normalmente establece su nombre de usuario y correo electrónico):

 $ git config --global push.default simple 

por ejemplo.

Si usa simple , git empujará su twig actual (por ejemplo, trouble ) al control remoto, pidiendo al control remoto que actualice su trouble . Es decir, estos impulsos funcionan por nombres de sucursales y demandas simple que los dos gits diferentes aquí (el suyo, en su sistema y el suyo, en el sistema remoto) usan el mismo nombre de bifurcación.

Esto responde a la pregunta "¿qué nombre de la twig le pide a su idiota que se actualice su git?": Si está presionando un trouble sucursal, su git le pedirá a su git que actualice su trouble llamado bifurcación. Si tu git está presionando a tu master , pedirá a su git que actualice su master . La twig que presionarás, si no nombras una twig, es tu twig actual, y no hay cosas complicadas como tener una twig en tu repository que deletree Raymond-Luxury-Yacht, pero en el control remoto, se escribe Throatwobbler- Manglar

Eso es bastante simple, y es por eso que esto se llama simple . Hay otras cuatro opciones, pero las dejaré para esta publicación.

¿Qué salió mal aquí?

Considera las líneas 35-37:

 35. The-Dorfchester:gordo.dev ajh$ git push trouble trouble 36. fatal: 'trouble' does not appear to be a git repository 37. fatal: Could not read from remote repository. 

El command git push toma un control remoto como su primera palabra después de push . La palabra trouble , tratada como un control remoto, no funciona (no tiene un control remoto llamado trouble ). El código de push está lleno de equipaje histórico, por lo que trató de usar trouble como una URL después de eso, y tampoco funcionó.

(Voy a omitir la salida de git show-branch porque algo lo ha destrozado, eliminando el espacio en blanco inicial, lo que hace que sea muy difícil de leer).

git checkout , y qué salió mal aquí

El command de checkout de Git es (en mi opinión) innecesariamente complejo ya que tiene demasiados modos de operación. Sería less confuso si git usara commands separados para "cambiar a una twig diferente" frente a "verificar files específicos de alguna twig, sin cambiar la twig actual". Pero todos están agrupados en un command de git checkout , así que veamos las líneas 75-79:

 75. The-Dorfchester:gordo.dev ajh$ git checkout staging master 76. error: pathspec 'staging' did not match any file(s) known to git. 77. error: pathspec 'master' did not match any file(s) known to git. 78. The-Dorfchester:gordo.dev ajh$ git checkout staging 79. error: pathspec 'staging' did not match any file(s) known to git. 

La forma más común de pago es git checkout branch-name , pero en este caso, está invocando un git checkout diferente, que es git checkout [ branch-name ] [ -- ] path1 path2 ... pathN , pero puede dejar fuera el -- . Como la staging no es un nombre de twig válido, en su lugar se interpreta como una path . (No importa que el master sea un nombre de sucursal válido, porque el staging fue en la única position de argumento donde el git checkout permite un nombre de sucursal).

En la línea 80 recibiste un error diferente:

 80. The-Dorfchester:gordo.dev ajh$ git checkout master 81. error: Your local changes to the following files would be overwritten by checkout: 82. .idea/workspace.xml 83. wp-content/themes/goTheme2015/blog-loadMore.php 84. wp-content/themes/goTheme2015/home.php 85. wp-content/themes/goTheme2015/stylesheets/layout.css 86. wp-content/themes/goTheme2015/stylesheets/layout.scss 87. Please, commit your changes or stash them before you can switch branches. 

En este momento está (todavía) en trouble sucursal y le pidió a git que se mude al master sucursal. Para pasar de una twig a otra, git debe replace el contenido de algunos files en su tree de trabajo. ¿Qué files?

La respuesta es un poco complicada, pero en este caso, los errores se producen para (al less) algunos files como .idea/workspace.xml que (1) se almacenan en la confirmación más reciente en la twig actual ; (2) también están en la sugerencia de punta de la nueva twig; y (3) los contenidos de ese file en la nueva twig son diferentes de los contenidos de ese file en la confirmación actual.

Si el file en el tree de trabajo coincidía con el file en la confirmación actual, git se sentiría seguro al borrar la versión del tree de trabajo y replacela con la versión de confirmación de sugerencia de cambio a master (en este caso). Pero estos files en su tree de trabajo no coinciden con la confirmación actual. Vimos eso en el git checkout -b original de git checkout -b cuando ejecutó el git status .

Entonces, git se negó a cambiar las twigs, solicitando que usted confirme sus files modificados o use git stash para enviarlos (la diferencia es que git stash compromete en ninguna twig, en lugar de en la twig actual).

OK, entonces finalmente los cometiste

Ahora llegamos a las líneas 89-91:

 89. The-Dorfchester:gordo.dev ajh$ git commit -am "idk" 90. [trouble 820decb] idk 91. 5 files changed, 39 insertions(+), 93 deletions(-) 

Esto hizo una nueva confirmación, en el trouble sucursal actual. Luego movió la twig para apuntar a la nueva confirmación, cuyo SHA-1 de 40 caracteres comienza con 820decb .

Ahora llegamos a la línea 92:

 92. The-Dorfchester:gordo.dev ajh$ git push master 

Esto le pide a su idiota que presione el control remoto llamado master . No hay uno, y obtienes el mismo error que antes. Todos estos también escupieron ese enorme y molesto post "configure su push.default ", que nos lleva a la línea 119, y también a 125:

 119. The-Dorfchester:gordo.dev ajh$ git pull master ... 125. The-Dorfchester:gordo.dev ajh$ git push --set-upstream master trouble 

Ambos tienen el mismo problema que antes: la palabra master está en la ranura para un nombre remoto, pero no es un control remoto válido.

Esto nos lleva a la línea 131:

 131. The-Dorfchester:gordo.dev ajh$ git push --set-upstream staging trouble 

Finalmente, ¡un command Git le gusta! 🙂 Esta staging en staging time está en la ranura para un control remoto, y hay trouble en la ranura para un "refspec", y ambos funcionan. (El --set-upstream le dice a git push que una vez que el push tenga éxito, debe registrar el trouble staging como la "twig ascendente" para su staging local. Para muchas palabras sobre lo que significa todo esto, vea esta respuesta ).

Vamos a omitir una salida más y procedemos a la línea 154 (y sus posts de éxito):

 154. The-Dorfchester:gordo.dev ajh$ git checkout master 155. Switched to branch 'master' 156. Your branch is behind 'staging/master' by 16 commits, and can be fast-forwarded. 157. (use "git pull" to update your local branch) 

Este funciona Sin embargo, no es por el éxito del git push : tiene éxito esta vez porque finalmente se ha comprometido en su twig de trouble . Eso consiguió que los files modificados se almacenaran de forma segura dentro del repository, ya que ese nuevo compromiso cuyo 40 "carácter verdadero" ID SHA-1 comienza con 820decb .

Una vez que se cometieron, para que tu tree de trabajo estuviera limpio, git hubiera estado bien con el git checkout master . También transfirió su trouble a su control remoto llamado "assembly", dándole el nombre de trouble también, pero eso no es importante para el paso git checkout master .

La línea 155 reconoce el éxito. La línea 156 es el resultado del git status , que le dice que su master está detrás de su flujo ascendente (que su git llama staging/master ) por 16 confirmaciones (hay 16 confirmaciones que tuvieron que no hizo) y no está "adelantado" está aguas arriba. Nuevamente, vea mi otra respuesta (ya enlazada arriba) para más sobre esto.

Luego ejecutó git pull , que en realidad es solo git fetch seguido de git merge (se trata de un contenedor de conveniencia para estos dos pasos, pero resulta que el segundo es un paso incorrecto para la mayoría de las personas, por lo que es configurable). La git merge hizo una git merge "avance rápido", lo que significa que no tenías nuevas confirmaciones, y tenían confirmaciones nuevas, por lo que tu git solo pudo "deslizar la twig hacia adelante" hacia la nueva bifurcación, agregando su nuevo DAGlet a su DAG existente sin ningún problema.

Eso nos lleva hasta la línea 388 (y sus líneas de respuesta):

 388. The-Dorfchester:gordo.dev ajh$ git commit -am "pull from staging/master to master, idk" 389. On branch master 390. Your branch is up-to-date with 'staging/master'. 391. nothing to commit, working directory clean 

Este command de git commit no encontró nada para confirmar, y no hizo ningún commit nuevo. Su master sucursal no tiene nuevos compromisos agregados y la sugerencia de sucursal es la misma que antes.

 392. The-Dorfchester:gordo.dev ajh$ git push master 

Este es el mismo error que antes: primero, git push quiere el nombre de un control remoto. La gran cantidad de push.default nos lleva a:

 419. The-Dorfchester:gordo.dev ajh$ git push staging master 

Esta es correcta, pero acabamos de ver que su git commit no agregó nuevas confirmaciones. Así que tu idiota llama la atención sobre la staging en staging y descubre que no hay nada que hacer:

 420. Everything up-to-date 

y no hace nada (y tiene éxito).

(Por cierto, el gran push.default sobre push.default se omite aquí porque le dio a git push tanto un control remoto como un refspec. La configuration push.default es lo que debería hacer el push si no da un refspec, es decir, si lo da solo un control remoto, o no le da nada en absoluto. Si le da a git push nada en absoluto, descubrirá el control remoto para usar en function de la configuration de subida de la bifurcación actual).


1 ¿Cuántos significados dependen de cómo se countn algunas divisiones más finas? Vea esta pregunta para más.

2 Tenga en count que DAGlet es mi propio invento, por lo que si comienza a usarlo, es posible que deba definirlo para su público.