git push --force-with-lease vs. --force


183

Estoy tratando de entender la diferencia entre

git push --force

y

git push --force-with-lease

¿Supongo que este último solo empuja al control remoto si el control remoto no tiene confirmaciones que la rama local no tiene ?


la " rama de seguimiento remoto local ". Básicamente, significa que el control remoto debe tener el aspecto que espera su cliente. git help pushtiene casos de uso que explican su propósito (básicamente para evitar que arruines un cambio que alguien acaba de presionar). Lo que no me queda claro es cómo funciona la rama de seguimiento remoto. Pero presumiblemente, por lo general, tendrá que verse exactamente como se veía la última vez que hizo una fetcho pullsin nuevas confirmaciones.
zzxyz

44
@zzxyz: la implementación real --force-with-leasees similar a la de las instrucciones de comparar e intercambiar en las CPU modernas: el que desea que se produzca el intercambio proporciona el valor esperado y el nuevo valor. El sistema que realiza el intercambio compara el valor esperado con el valor actual real, y realiza el intercambio si y solo si los dos son iguales. Con git push, el valor esperado es lo que esté en el nombre de seguimiento remoto, por ejemplo, git push --force-with-lease origin Xenvía el suyo origin/Xjunto con el nuevo valor deseado; originGit te dice si hizo el intercambio o no.
torek

1
Si el Git originhizo el intercambio, ya está. De lo contrario, puede ejecutar git fetch originpara recoger el nuevo valor actual, modificar sus cambios si es necesario y ejecutar otra comparación de forzar con el arrendamiento para volver a intentarlo.
torek

Respuestas:


173

force sobrescribe una sucursal remota con su sucursal local.

--force-with-leasees una opción más segura que no sobrescribirá ningún trabajo en la rama remota si se agregan más confirmaciones a la rama remota (por otro miembro del equipo o compañero de trabajo o lo que sea). Asegura que no sobrescribas el trabajo de alguien más por empujar a la fuerza.

Creo que su idea general sobre el comando es correcta. Si la rama remota tiene el mismo valor que la rama remota en su máquina local, sobrescribirá la remota. Si no tiene el mismo valor, indica un cambio que otra persona realizó en la rama remota mientras trabajaba en su código y, por lo tanto, no sobrescribirá ningún código. Obviamente, si hay confirmaciones adicionales en remoto, los valores no serán los mismos.

Solo pienso --force-with-leaseen la opción de usar cuando quiero asegurarme de no sobrescribir ningún código de compañeros de equipo. Muchos equipos de mi empresa utilizan --force-with-leasecomo opción predeterminada para una prueba de fallos. Es innecesario en la mayoría de las circunstancias, pero le ahorrará mucho dolor de cabeza si sobrescribe algo que otra persona contribuyó al control remoto.

Estoy seguro de que miró los documentos, pero puede haber alguna explicación más detallada contenida aquí:

https://git-scm.com/docs/git-push


39

Buscando una respuesta basada en fuentes creíbles y / u oficiales.

El "comparar e intercambiar" mencionado por torek en los comentarios y en su otra respuesta se ilustra aún más con las fuentes del propio Git .

el último solo empuja al control remoto si el control remoto no tiene confirmaciones que la rama local no tiene?

Esa característica se introdujo en esta confirmación (diciembre de 2013, Git v1.8.5-rc0)

--force-with-lease protegerá todas las referencias remotas que se actualizarán al exigir que su valor actual sea el mismo que algún valor predeterminado razonable, a menos que se especifique lo contrario;

Por ahora, "algún valor predeterminado razonable" se define provisionalmente como " el valor de la rama de seguimiento remoto que tenemos para la referencia de la actualización remota ", y es un error si no tenemos una rama de seguimiento remoto.

Entonces "arrendamiento" significa:

" force-with-lease": Asumes que tomaste el contrato de arrendamiento en la referencia cuando buscaste para decidir cuál debería ser el historial modificado, y puedes retroceder solo si el contrato de arrendamiento no se ha roto.

Las fuentes aún mencionan "cas":

  • Esta opción se llamaba originalmente " cas" (para "comparar e intercambiar"), el nombre que a nadie le gustaba porque era demasiado técnico.
  • El segundo intento lo llamó "lockref" (porque es conceptualmente como presionar después de tomar un bloqueo) pero la palabra "lock" fue odiada porque implicaba que puede rechazar el empuje de otros, que no es la forma en que funciona esta opción.
  • Esta ronda lo llama "fuerza con arrendamiento".
    Asume que tomó el contrato de arrendamiento en la referencia cuando buscó para decidir cuál debería ser el historial modificado, y puede retroceder solo si el contrato de arrendamiento no se ha roto.

Entonces: " git push --force-with-leasevs. --force"

Como mencioné en " push --force-with-leasepor defecto ", como menciona Git 2.13 (Q2 2017), que la opción --force-with-leasepuede ignorarse si se ejecuta un proceso en segundo plano (como los que se encuentran en un IDE con un complemento de Git) git fetch origin.
En ese caso, --forceprevalece.


29

git push --force es destructivo porque sobrescribe incondicionalmente el repositorio remoto con lo que sea que tenga uno localmente. El empuje de git --force está fuertemente desaconsejado, ya que puede destruir otras confirmaciones ya enviadas a un repositorio compartido. Una de las causas más comunes de los empujes forzados es cuando nos vemos obligados a rebasear una rama.

Por ejemplo. Tenemos un proyecto con una rama de características en la que tanto Alice como Bob van a trabajar. Ambos clonan este repositorio y comienzan a trabajar. Al principio, Alice completa su parte de la función y la lleva al repositorio principal. Todo esto está muy bien. Bob también termina su trabajo, pero antes de subirlo nota que algunos cambios se han fusionado en master. Queriendo mantener un árbol limpio, realiza un rebase contra la rama maestra. Por supuesto, cuando vaya a impulsar esta rama rebaseada, será rechazada. Sin embargo, sin darse cuenta de que Alice ya ha presionado su trabajo, realiza una fuerza de empuje. Desafortunadamente, esto borrará todo registro de los cambios de Alice en el repositorio central.

Lo que hace --force-with-lease es negarse a actualizar una sucursal a menos que sea el estado que esperamos; es decir, nadie ha actualizado la rama aguas arriba. En la práctica, esto funciona comprobando que la referencia ascendente es lo que esperamos, porque las referencias son hashes y codifican implícitamente la cadena de padres en su valor.

Aquí hay una buena publicación sobre git push --force y git push --force-with-lease.


2
lo que no entiendo: si rebasas con master, ¿deberías poder presionar sin él --force-with-lease ? ¿Por qué es --force-with-leasenecesario después de rebasar con el maestro?
Alexander Mills el

3
@AlexanderMills puede tener alguna posibilidad de causar problemas. Si usted es la última persona que empuja esto a dominar, entonces no hay problema, pero hay otra persona que trabaja con otra tarea, entonces puede causar un problema grave. Digamos que al principio A - B - C estaba en el maestro, Al principio Alice lo hace A - B - C - D - E y al mismo tiempo Bob lo hace A - B - C - F - G. Si Alice es la última persona que empuja en master después de rebasar A - B - C - D - E - F - G no causará ningún problema, pero otra persona Tom intentará presionar A - B - C - D - E - R al mismo tiempo en que ocurra un desastre maestro. !
Shakil

3
--forceno pierde confirmaciones, solo las separa. Pueden ser resucitados por sus hashes, como en git checkout <SHA>o git reset --hard <SHA>, suponiendo que el mantenedor del repositorio remoto no realice ninguna operación de GC o poda.
Keith Russell

1
"El empuje de git: la fuerza se desaconseja fuertemente, ya que puede destruir otras confirmaciones ya enviadas a un repositorio compartido", esta declaración está fuertemente basada en la opinión. Hacer un empuje forzado es parte de un flujo de trabajo de revisión de código de rebase git normal. Además, como ya se mencionó, puede recuperar las confirmaciones incluso después de hacer esto.
Étienne

9

Suponiendo que cualquier gancho de pre-recepción en el servidor acepte el envío, esto siempre tendrá éxito:

git push --force

Mientras que esto ejecuta una verificación específica del lado del cliente antes de continuar:

git push --force-with-lease

Puede ejecutar la verificación específica usted mismo manualmente. Aquí está el algoritmo de "verificación de arrendamiento":

  1. Descubre tu rama actual.

  2. Ejecutar git for-each-ref refs/remotes. Tome nota del commit-id que su cliente git cree que corresponde al estado ascendente de su rama actual.

Por ejemplo, si está en la rama "foo", tome nota del commit-id asociado con "refs / remotes / origin / foo".

  1. Determine la ID de confirmación real de la rama remota en el servidor git ascendente en este momento.

  2. Solo deje que continúe el "git push" si los commit-id que extrajo del paso 2 y el paso 3 están de acuerdo. En otras palabras, solo proceda si la noción de flujo ascendente de su clon git local está de acuerdo con el flujo ascendente real.

Aquí hay una triste implicación: dado que git fetchactualiza todas las referencias bajo "refs / remotes / origin / *" a sus últimas versiones, esta combinación de comandos es esencialmente idéntica a git push --force:

git fetch

# The command below behaves identically to "git push --force"
# if a "git fetch" just happened!

git push --force-with-lease

Para evitar esta debilidad inherente git push --force-with-lease, trato de no correr nunca git fetch. En su lugar, siempre ejecuto git pull --rebasecada vez que necesito sincronizar con el flujo ascendente, ya que git pullsolo actualiza una única referencia bajo referencias / controles remotos, manteniendo --force-with-leaseútil la "concesión" .


1
si el cheque es del lado del cliente, ¿existe la posibilidad de una condición de carrera? es decir, ¿alguien más empuja los cambios después del chequeo pero antes del empuje forzado?
craq

2

La fuerza con arrendamiento no es necesariamente segura. Simplemente funciona como Sylvie dijo. Una nota: en git, una rama es solo un puntero en una confirmación. Y commits apunta a cero o más commits padre. Incluso si cambiaste la rama por completo con un reinicio de git duro y un empuje forzado o un empuje con - - fuerza con arrendamiento sin querer, eso no es necesariamente un gran problema. Puede usar su registro local de git para ver cómo ha cambiado su punta local en las ramas (¿Dónde estaba HEAD en ese momento?) Y restablecer y presionar la rama nuevamente. Luego, solo pierde nuevas confirmaciones en la rama remota, pero incluso los miembros del equipo pueden restaurarlas.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.