Git reset --hard y empuje al repositorio remoto

200

Tenía un repositorio que tenía algunos errores de confirmación (D, E y F para este ejemplo).

ABCDEF maestro y origen / maestro

He modificado el repositorio local específicamente con a git reset --hard. Tomé una rama antes del reinicio, así que ahora tengo un repositorio que se ve así:

A-B-C master  
     \ D-E-F old_master

A-B-C-D-E-F origin/master

Ahora necesitaba algunas partes de esos commits malos, así que escogí los bits que necesitaba e hice algunos commits nuevos, así que ahora tengo lo siguiente localmente:

A-B-C-G-H master
     \ D-E-F old_master

Ahora quiero llevar este estado de cosas al repositorio remoto. Sin embargo, cuando trato de hacer un git pushGit cortésmente me quita el cepillo:

$ git push origin +master:master --force  
Total 0 (delta 0), reused 0 (delta 0)  
error: denying non-fast forward refs/heads/master (you should pull first)  
To [email protected]:myrepo.git  
! [remote rejected] master -> master (non-fast forward)  
error: failed to push some refs to '[email protected]:myrepo.git'  

¿Cómo consigo que el repositorio remoto tome el estado actual del repositorio local?

robertpostill
fuente
2
El es un "casi" duplicado de varias "cómo presiono las preguntas de historial modificadas", por ejemplo, vea la respuesta aquí stackoverflow.com/questions/253055/…
CB Bailey
2
Eso es cierto y busqué en StackOverflow una respuesta antes de publicar. Sin embargo, mi búsqueda solo había encontrado respuestas en las que un empujón git --force solucionó el problema. Gracias por enlazar a tu publicación :)
robertpostill
2
Pronto (git1.8.5, Q4 2013) podrá hacerlo con git push -forcemás cuidado .
VonC

Respuestas:

287

Si forzar un empuje no ayuda ( " git push --force origin'o' git push --force origin master" debería ser suficiente), que podría significar que el servidor remoto no se niega empujones de avance rápido o bien a través receive.denyNonFastForwards config variable (ver git config página del manual de descripción), o a través de la actualización / pre-recibir gancho.

Con Git anterior, puede evitar esa restricción eliminando " git push origin :master" (vea el ':' antes del nombre de la rama) y luego volviendo a crear " git push origin master" la rama dada.

Si no puede cambiar esto, entonces la única solución sería en lugar de reescribir el historial para crear una confirmación que revierta los cambios en DEF :

ABCDEF - [(DEF) ^ - 1] maestro

Origen ABCDEF / maestro
Jakub Narębski
fuente
2
@ JakubNarębski, gracias. get revert HEAD~Nayudado. Nes el número de confirmaciones Por ejemplo, si necesito el commit anterior, lo usarégit revert HEAD~1
Maksim Dmitriev
1
Y tenga en cuenta que romperá a todos los demás maestros locales al hacer esto.
Tom Brito
24

Para complementar la respuesta de Jakub, si tiene acceso al servidor remoto de git en ssh, puede ir al directorio remoto de git y configurar:

user@remote$ git config receive.denyNonFastforwards false

Luego regrese a su repositorio local, intente nuevamente hacer su compromiso con --force:

user@local$ git push origin +master:master --force

Y finalmente revierta la configuración del servidor en el estado protegido original:

user@remote$ git config receive.denyNonFastforwards true
Jealie
fuente
Consulte también pete.akeo.ie/2011/02/denying-non-fast-forward-and.html para obtener información personalizada de sourceforge sobre esto.
hlovdal
En viesta publicación SO se proporcionan instrucciones detalladas sobre cómo deshabilitar el uso de denyNonFastForwards : stackoverflow.com/a/43721579/2073804
ron190
2

En lugar de arreglar su rama "maestra", es mucho más fácil intercambiarla con su "maestra deseada" cambiando el nombre de las ramas. Ver https://stackoverflow.com/a/2862606/2321594 . De esta forma, ni siquiera dejaría rastros de múltiples registros de reversión.

Ayuda en
fuente
1

Todo el negocio de reinicio de git parecía demasiado complicado para mí.

Así que hice algo similar para obtener mi carpeta src en el estado en el que tuve algunas confirmaciones hace

# reset the local state
git reset <somecommit> --hard 
# copy the relevant part e.g. src (exclude is only needed if you specify .)
tar cvfz /tmp/current.tgz --exclude .git  src
# get the current state of git
git pull
# remove what you don't like anymore
rm -rf src
# restore from the tar file
tar xvfz /tmp/current.tgz
# commit everything back to git
git commit -a
# now you can properly push
git push

De esta manera, el estado de las cosas en el src se mantiene en un archivo tar y git se ve obligado a aceptar este estado sin demasiados problemas, básicamente el directorio src se reemplaza por el estado que tenía hace varios commits.

Wolfgang Fahl
fuente
0

Para los usuarios de GitHub, esto funcionó para mí:

  1. En cualquier regla de protección de rama donde desee realizar el cambio, asegúrese de que Permitir empujes forzados esté habilitado
  2. git reset --hard <full_hash_of_commit_to_reset_to>
  3. git push --force

Esto "corregirá" el historial de la rama en su máquina local y el servidor GitHub, pero cualquiera que haya sincronizado esta rama con el servidor desde la mala confirmación tendrá el historial en su máquina local. Si tienen permiso para ingresar directamente a la rama, estas confirmaciones se mostrarán nuevamente cuando se sincronicen.

Todo lo que todos los demás deben hacer es el git resetcomando desde arriba para "corregir" la rama en su máquina local. Por supuesto, tendrían que tener cuidado con los compromisos locales realizados en esta rama después del hash objetivo. Cherry seleccione / respalde y vuelva a aplicarlos según sea necesario, pero si se encuentra en una rama protegida, entonces el número de personas que pueden comprometerse directamente con él probablemente sea limitado.

Jason Faulkner
fuente