¿Cómo revertir una confirmación de fusión que ya se ha enviado a la rama remota?

962

git revert <commit_hash>solo no funcionará. -mdebe especificarse, y estoy bastante confundido al respecto.

¿Alguien ha experimentado esto antes?

Yaz
fuente
3
Eche un vistazo a la respuesta a esta pregunta: stackoverflow.com/questions/2318777/…
eugen
2
Relacionado: ¿ Deshacer una fusión Git? .
El enlace aquí es el mejor ejemplo que ilustra la reversión de la confirmación fusionada: christianengvall.se/undo-pushed-merge-git
SK Venkat
Este es un ejemplo de donde el diseño de gitno coincide con el git-flowflujo de trabajo -ish que todos usan. Si se ha developdesprotegido, por supuesto , desea revertir la rama de la función de 2 confirmaciones que introdujo un error y no la rama de desarrollo compartida durante años. Se siente ridículo necesitando recogerlo -m 1.
pkamb
2
Solo otra sugerencia que nunca se me ocurrió antes: si una de las listas de commits de las ramas es pequeña, es posible que se sienta más cómodo revocando los commits individuales en lugar de una rama completa de commits.
Sridhar Sarnobat

Respuestas:

1154

La -mopción especifica el número principal . Esto se debe a que un commit de fusión tiene más de un padre, y Git no sabe automáticamente qué padre era la línea principal y qué padre era la rama que desea desunir.

Cuando vea una confirmación de fusión en la salida de git log, verá a sus padres listados en la línea que comienza con Merge:

commit 8f937c683929b08379097828c8a04350b9b8e183
Merge: 8989ee0 7c6b236
Author: Ben James <[email protected]>
Date:   Wed Aug 17 22:49:41 2011 +0100

Merge branch 'gh-pages'

Conflicts:
    README

En esta situación, git revert 8f937c6 -m 1obtendrá el árbol tal como estaba 8989ee0y git revert -m 2restablecerá el árbol tal como estaba 7c6b236.

Para comprender mejor las ID principales, puede ejecutar:

git log 8989ee0 

y

git log 7c6b236
Ben James
fuente
127
de dos números 8989ee0, a 7c6b236cuál ir. ¿Cómo lo entendería?
Arup Rakshit
12
Después de revertir, ¿no creo que uno pueda corregir fácilmente el código en la rama fuente y fusionar nuevamente? kernel.org/pub/software/scm/git/docs/howto/…
IsmailS
10
Mientras buscaba en Google una mejor explicación, encontré este artículo que pensé que hizo un gran trabajo al repasar los detalles. Después de leer, descubrí que lo que realmente estaba buscando era el comando RESET, seguido de un empuje forzado. Quizás ayude a alguien más. atlassian.com/git/tutorials/…
Funktr0n
46
@ArupRakshit si corres git log 8989ee0y git log 7c6b236deberías saber la respuesta.
BMW
44
git log: se fusiona para ver todas las fusiones y git log: no se fusiona para ver el historial sin fusiones. Fusionar una rama trae el historial de la rama fusionada al objetivo y hace que sea difícil distinguirlo usando el registro simple de git
Alex Punnen
370

Aquí hay un ejemplo completo con la esperanza de que ayude a alguien:

git revert -m 1 <commit-hash> 
git push -u origin master

¿Dónde <commit-hash>está el hash de confirmación de la fusión que desea revertir y, como se indica en la explicación de esta respuesta , -m 1indica que desea revertir al árbol del primer padre antes de la fusión?

La git revert ...línea esencialmente confirma sus cambios, mientras que la segunda línea hace públicos sus cambios empujándolos a la rama remota.

Saheed
fuente
21
Creí que el git revertcomando ya había confirmado el objeto de confirmación creado. Para que eso no suceda, deberá ingresar la --no-commitbandera
Delfic
2
Como mencionó @Delfic, el commit ya está administrado por la primera línea (necesitaba un: wq para validarlo), por lo que la segunda línea no es necesaria.
eka808
1
esto es confuso. solo hay 2 líneas y no git commit ... ¿alguien puede editar?
Jay Random
176

Ben le ha dicho cómo revertir una confirmación de fusión, pero es muy importante que se dé cuenta de que al hacerlo

"... declara que nunca querrá los cambios de árbol introducidos por la fusión. Como resultado, las fusiones posteriores solo traerán cambios de árbol introducidos por commits que no son ancestros de la fusión revertida anteriormente. Esto puede o no ser lo que quieras " (página de manual de git-merge) .

Un mensaje de artículo / lista de correo vinculado desde la página de manual detalla los mecanismos y consideraciones que están involucrados. Solo asegúrese de comprender que si revierte la confirmación de fusión, no puede volver a fusionar la rama más tarde y esperar que vuelvan los mismos cambios.

Ryan Stewart
fuente
81
Pero puede revertir la reversión para recuperarlos si realmente es necesario.
Dalore
55
Gracias. Es muy útil saberlo como el caso de uso para deshacer una fusión, debido a un error, por ejemplo, y luego volver a fusionar toda la rama una vez que se soluciona el error, es común.
Componente 10
3
Si eres como yo y luego quisiste la fusión, puedes revertir la reversión o elegir el cambio que revertiste.
UnitasBrooks
En mi situación, me tocó tener que 'revertir la reversión' para obtener mi problema de cambios. Recoger cerezas podría ser una forma más ordenada? Lo intentaré la próxima vez ...
Steven Anderson
79

Podrías seguir estos pasos para revertir los commits incorrectos o restablecer tu rama remota de nuevo a HEAD / estado correcto.

  1. pagar la sucursal remota al repositorio local.
    git checkout development
  2. copie el hash de confirmación (es decir, la identificación de la confirmación inmediatamente antes de la confirmación incorrecta) del registro de git git log -n5

    salida:

    commit 7cd42475d6f95f5896b6f02e902efab0b70e8038 "Fusionar rama 'incorrecto-compromiso' en 'desarrollo'"
    commit f9a734f8f44b0b37ccea769b9a2fd774c0f0c012 "esto es un compromiso incorrecto"
    commit 3779ab50e72908da92d2c

  3. restablecer la rama al hash de confirmación copiado en el paso anterior
    git reset <commit-hash> (i.e. 3779ab50e72908da92d2cfcd72256d7a09f446ba)

  4. ejecute el git statuspara mostrar todos los cambios que fueron parte de la confirmación incorrecta.
  5. simplemente corre git reset --hardpara revertir todos esos cambios.
  6. empuje a la fuerza su sucursal local a control remoto y observe que su historial de confirmación está limpio como estaba antes de contaminarse.
    git push -f origin development
ssasi
fuente
44
¿Qué pasa si mientras tanto 20 desarrolladores sacaron la última fusión de desarrollo?
Ewoks el
2
No forzaría a impulsar una rama de desarrollo cuando hay 20 desarrolladores en el equipo que usan esa rama. :) En ese caso, es aconsejable hacer una confirmación de reversión.
ssasi
44
Esta es una muy buena solución cuando trabajas solo o estás seguro de que ningún otro desarrollador hizo los compromisos que arruinaste
Kyle B
52
git revert -m 1 <merge-commit>
Neeraj Kumar
fuente
2
Esta respuesta carece de muchos detalles. Quizás por eso es bueno.
Gustavo Straube
30

Para mantener el registro limpio ya que no pasó nada (con algunas desventajas con este enfoque (debido a push -f)):

git checkout <branch>
git reset --hard <commit-hash-before-merge>
git push -f origin HEAD:<remote-branch>

'commit-hash-before-merge' proviene del registro (git log) después de la combinación.

nvd
fuente
Sugerencia: si está haciendo esto en su empresa, es posible que no tenga permiso.
eneski
3
nunca haga un push -fen un repositorio compartido
Baptiste Mille-Mathias
17

A veces, la forma más efectiva de retroceder es retroceder y reemplazar.

git log

Use el segundo hash de confirmación (hash completo, al que desea volver, antes del error enumerado) y luego vuelva a ramificar desde allí.

git checkout -b newbranch <HASH>

Luego elimine la rama anterior, copie la rama nueva en su lugar y reinicie desde allí.

git branch -D oldbranch
git checkout -b oldbranch newbranch

Si se ha transmitido, elimine la rama antigua de todos los repositorios, empuje la rama renovada a la más central y vuelva a bajarla a todos.

ppostma1
fuente
44
La advertencia sobre la transmisión realmente debería ser más explícita sobre cuán horrible es esta idea. Esto corromperá la versión de todos de esa rama y solo es realmente útil si está trabajando con un repositorio remoto (github / bitbucket) al que solo usted tiene acceso.
RobbyD
No es tan malo como enviar archivos de configuración modificados a la producción. No se corromperá, es solo una nueva ramificación de una confirmación anterior, por lo que es una forma redonda de mover el puntero de las ramas a una versión anterior. Esperemos que solo
afecte el
5

Si desea revertir una mergeconfirmación, esto es lo que debe hacer.

  1. Primero, verifique git logpara encontrar la identificación de su confirmación de fusión. También encontrará múltiples identificadores principales asociados con la fusión (ver imagen a continuación).

ingrese la descripción de la imagen aquí

Anote la identificación de confirmación de fusión que se muestra en amarillo. Las ID principales son las que se escriben en la siguiente línea como Merge: parent1 parent2. Ahora...

Cuento:

  1. Cambie a la rama en la que se realizó la fusión. Luego, simplemente haga lo git revert <merge commit id> -m 1que abrirá una viconsola para ingresar el mensaje de confirmación. Escribir, guardar, salir, listo!

Larga historia:

  1. Cambie a la rama en la que se realizó la fusión. En mi caso, es la testrama y estoy tratando de eliminarla feature/analytics-v3.

  2. git revertes el comando que revierte cualquier confirmación. Pero hay un truco desagradable al revertir un mergecommit. Debe ingresar la -mbandera; de lo contrario, fallará. A partir de ahora, debe decidir si desea revertir su sucursal y hacer que se vea exactamente como estaba parent1o parent2mediante:

git revert <merge commit id> -m 1(vuelve a parent2)

git revert <merge commit id> -m 2(vuelve a parent1)

Puede registrar a estos padres para averiguar en qué dirección quiere ir y esa es la raíz de toda la confusión.

saran3h
fuente
5

Todas las respuestas ya cubrieron la mayoría de las cosas, pero agregaré mis 5 centavos. En resumen, revertir una confirmación de fusión es bastante simple:

git revert -m 1 <commit-hash>

Si tiene permiso, puede enviarlo directamente a la rama "maestra"; de lo contrario, simplemente empujarlo a su rama "revertir" y crear una solicitud de extracción.

Puede encontrar más información útil sobre este tema aquí: https://itcodehub.blogspot.com/2019/06/how-to-revert-merge-in-git.html

xproph
fuente
1

Encontré que crear un parche inverso entre dos puntos finales conocidos y aplicar ese parche funcionaría. Esto supone que ha creado instantáneas (etiquetas) fuera de su rama maestra o incluso una copia de seguridad de su rama maestra, por ejemplo master_bk_01012017.

Digamos que la rama de código que fusionó en master fue mycodebranch.

  1. Checkout master.
  2. Cree un parche inverso binario completo entre el maestro y su copia de seguridad. git diff --binary master..master_bk_01012017 > ~/myrevert.patch
  3. Revisa tu parche git apply --check myrevert.patch
  4. Aplicar parche con cierre de sesión git am --signoff < myrevert.patch
  5. Si necesita volver a introducir este código una vez que se haya solucionado, deberá ramificar el maestro revertido y finalizar la reparación de la ramificación git branch mycodebranch_fix git checkout mycodebranch_fix
  6. Aquí debe encontrar la clave SHA para la reversión y revertir la reversión git revert [SHA]
  7. Ahora puede usar su mycodebranch_fix para solucionar los problemas, confirmar y volver a fusionar en master una vez hecho.
alexplain
fuente
1

La respuesta correctamente marcada funcionó para mí, pero tuve que pasar un tiempo para determinar qué está sucediendo ... Así que decidí agregar una respuesta con pasos simples y directos para casos como el mío ...

Digamos que tenemos las ramas A y B ... Fusionaste la rama A en la rama B y empujaste la rama B hacia sí, así que ahora la fusión es parte de ella ... Pero quieres volver al último commit antes de la fusión ... ¿Qué hacer? ¿tú lo haces?

  1. Vaya a su carpeta raíz git (la carpeta del proyecto generalmente) y use git log
  2. Verá el historial de confirmaciones recientes: las confirmaciones tienen propiedades de confirmación / autor / fecha, mientras que las fusiones también tienen una propiedad de fusión, así que las verá así:

    commit: <commitHash> Merge: <parentHashA> <parentHashB> Author: <author> Date: <date>

  3. Use git log <parentHashA>y git log <parentHashB>, verá los historiales de confirmación de esas ramas principales, las primeras confirmaciones de la lista son las últimas.

  4. Tome la <commitHash>confirmación que desee, vaya a su carpeta raíz git y úsela git checkout -b <newBranchName> <commitHash>, eso creará una nueva rama a partir de la última confirmación que haya elegido antes de la fusión. ¡Voila, listo!
Божидар Йовчев
fuente
0

También me enfrenté a este problema en un RP que se fusionó con la rama maestra de un repositorio de GitHub.

Dado que sólo quería modificar algunos archivos modificados, pero no cambia todo el traje del PR, que tenía que amendel merge commitcon git commit --am.

Pasos:

  1. Vaya a la rama que desea cambiar / revierta algunos archivos modificados
  2. Haz los cambios que quieras de acuerdo con los archivos modificados
  3. correr git add *ogit add <file>
  4. ejecutar git commit --amy validar
  5. correr git push -f

Por qué es interesante:

  • Mantiene el autor del RP comprometido sin cambios
  • No rompe el árbol git
  • Serás marcado como committer (el autor de commit de fusión permanecerá sin cambios)
  • Git actúa como si resolviera conflictos, eliminará / cambiará el código en archivos modificados como si le dijera manualmente a GitHub que no lo fusione tal como está
Maxime Lafarie
fuente
0

Encontré una buena explicación para Cómo revertir la fusión desde este enlace y copié pegué la explicación a continuación y sería útil en caso de que el siguiente enlace no funcione.

Cómo revertir una fusión defectuosa Alan ([email protected]) dijo:

Tengo una rama maestra. Tenemos una rama de eso en la que algunos desarrolladores están trabajando. Afirman que está listo. Lo fusionamos en la rama maestra. Rompe algo, así que revertimos la fusión. Hacen cambios al código. lo llevan a un punto en el que dicen que está bien y nos fusionamos nuevamente. Cuando se examina, encontramos que los cambios de código realizados antes de la reversión no están en la rama maestra, pero los cambios de código posteriores están en la rama maestra. y pidió ayuda para recuperarse de esta situación.

La historia inmediatamente después de la "reversión de la fusión" se vería así:

---o---o---o---M---x---x---W
              /
      ---A---B

donde A y B están en el desarrollo lateral que no fue tan bueno, M es la fusión que trae estos cambios prematuros a la línea principal, x son cambios no relacionados con lo que la rama lateral hizo y ya hizo en la línea principal, y W es el " revertir de la fusión M "(¿W no mira M al revés?). IOW, "diff W ^ .. W" es similar a "diff -RM ^ .. M".

Tal "reversión" de una fusión se puede hacer con:

$ git revert -m 1 M Después de que los desarrolladores de la rama lateral corrijan sus errores, el historial puede verse así:

---o---o---o---M---x---x---W---x
              /
      ---A---B-------------------C---D

donde C y D deben arreglar lo que se rompió en A y B, y es posible que ya tenga otros cambios en la línea principal después de W.

Si combina la rama lateral actualizada (con D en su punta), ninguno de los cambios realizados en A o B será el resultado, ya que fueron revertidos por W. Eso es lo que vio Alan.

Linus explica la situación:

Revertir un compromiso regular deshace efectivamente lo que hizo ese compromiso, y es bastante sencillo. Pero revertir una confirmación de fusión también deshace los datos que cambió la confirmación, pero no hace absolutamente nada para los efectos en la historia que tuvo la fusión. Por lo tanto, la fusión seguirá existiendo, y aún se verá como unir las dos ramas, y las futuras fusiones verán esa fusión como el último estado compartido, y la reversión que revirtió la fusión introducida no afectará en absoluto. Entonces un "revertir" deshace los cambios de datos, pero es mucho noun "deshacer" en el sentido de que no deshace los efectos de una confirmación en el historial del repositorio. Entonces, si piensa en "revertir" como "deshacer", siempre se perderá esta parte de las reversiones. Sí, deshace los datos, pero no, no deshace el historial. En tal situación, primero querrá revertir la reversión anterior, lo que haría que el historial se vea así:

---o---o---o---M---x---x---W---x---Y
              /
      ---A---B-------------------C---D

donde Y es la reversión de W. Tal "reversión de la reversión" se puede hacer con:

$ git revert W Este historial (ignorando posibles conflictos entre lo que W y W..Y cambiaron) sería equivalente a no tener W o Y en absoluto en el historial:

---o---o---o---M---x---x-------x----
              /
      ---A---B-------------------C---D

y fusionar la rama lateral nuevamente no tendrá conflictos derivados de una reversión anterior y una reversión de la reversión.

---o---o---o---M---x---x-------x-------*
              /                       /
      ---A---B-------------------C---D

Por supuesto, los cambios realizados en C y D todavía pueden entrar en conflicto con lo que hizo cualquiera de las x, pero eso es solo un conflicto de fusión normal.

MK446
fuente
-2

Como Ryan mencionó, git revertpodría hacer que la fusión sea difícil en el futuro, por lo que git revertpuede no ser lo que desea. Descubrí que usar el git reset --hard <commit-hash-prior-to-merge>comando es más útil aquí.

Una vez que haya realizado la parte de restablecimiento completo, puede forzar el empuje a la rama remota, es decir git push -f <remote-name> <remote-branch-name>, donde a <remote-name>menudo se nombra origin. Desde ese punto, puede volver a fusionar si lo desea.

Harry Wang
fuente
44
Cualquier cosa que implique empujes forzados es una mala idea a menos que usted sea el único que use el repositorio y sepa exactamente lo que está haciendo. Revertir con git revert y luego posiblemente revertir el revert con git revert (si necesita recuperar las cosas nuevamente) es una alternativa mucho más segura.
oyvind