Pregunta ¿Cómo revertir múltiples commit de git?


Tengo un repositorio git que se ve así:

A -> B -> C -> D -> HEAD

Quiero que el jefe de la rama apunte a A, es decir, quiero que B, C, D y HEAD desaparezcan y quiero que la cabeza sea sinónimo de A.

Parece que puedo intentar volver a establecer la base (no se aplica, ya que he modificado los cambios) o revertir. ¿Pero cómo revertir múltiples commits? ¿Revertiré uno a la vez? Es importante la orden?


602
2017-09-23 00:27


origen


Respuestas:


Expandiendo lo que escribí en un comentario

La regla general es que no debe reescribir (cambiar) el historial que ha publicado, porque alguien podría haber basado su trabajo en él. Si reescribe (modifica) el historial, tendría problemas para fusionar sus cambios y actualizarlos.

Entonces la solución es crear un nuevo compromiso cual revierte los cambios de lo que quieres deshacerte. Puedes hacer esto usando git revertir mando.

Usted tiene la siguiente situación:

A <- B <- C <- D <- maestro <- CABEZA

(las flechas aquí se refieren a la dirección del puntero: la referencia "principal" en el caso de confirmaciones, la confirmación superior en el caso del encabezado de bifurcación (ref rama) y el nombre de bifurcación en el caso de referencia HEAD).

Lo que necesita crear es lo siguiente:

A <- B <- C <- D <- [(BCD) ^ - 1] <- master <- HEAD

donde "[(BCD) ^ - 1]" significa la confirmación que revierte los cambios en las confirmaciones B, C, D. Las matemáticas nos dicen que (BCD) ^ - 1 = D ^ -1 C ^ -1 B ^ -1, entonces puede obtener la situación requerida usando los siguientes comandos:

$ git revert --no-commit D
$ git revert --no-commit C
$ git revert --no-commit B
$ git commit -m "the commit message"

La solución alternativa sería revisa  contenido de commit A, y cometer este estado:

$ git checkout -f A -- .
$ git commit -a

Entonces tendrías la siguiente situación:

A <- B <- C <- D <- A '<- maestro <- CABEZA

El compromiso A 'tiene los mismos contenidos que el compromiso A, pero es un compromiso diferente (mensaje de compromiso, padres, fecha de compromiso).

los solución de Jeff Ferland, modificado por Charles Bailey se basa en la misma idea, pero utiliza git reset:

$ git reset --hard A
$ git reset --soft @{1}  # (or ORIG_HEAD), which is D
$ git commit

914
2017-09-24 08:44



Para hacerlo solo debes usar el revertir comando, que especifica el rango de confirmaciones que desea revertir.

Teniendo en cuenta tu ejemplo, tendrías que hacer esto (asumiendo que estás en la rama 'master'):

git revert master~3..master

Esto creará una nueva confirmación en su local con la confirmación inversa de B, C y D (lo que significa que deshará los cambios introducidos por estas confirmaciones):

A <- B <- C <- D <- BCD' <- HEAD

150
2017-07-31 15:00



Manera limpia que encontré útil

git revert --no-commit HEAD~3..

Este comando revierte las últimas 3 confirmaciones con solo una confirmación.

Tampoco reescribe la historia.


53
2018-03-29 00:00



Similar a la respuesta de Jakub, esto le permite seleccionar fácilmente compromisos consecutivos para revertir.

# revert all commits from B to HEAD, inclusively
$ git revert --no-commit B..HEAD  
$ git commit -m 'message'

45
2017-07-31 18:49



git reset --hard a
git reset --mixed d
git commit

Eso actuará como una reversión para todos ellos a la vez. Da un buen mensaje de compromiso.


40
2017-09-23 00:45



Primero, asegúrese de que su copia de trabajo no se modifique. Entonces:

git diff HEAD commit_sha_you_want_to_revert_to | git apply

y luego solo cometer. No te olvides de documentar cuál es el motivo de la reversión.


22
2017-09-09 13:13



Estoy tan frustrado que esta pregunta no puede ser respondida. Todas las demás preguntas están relacionadas con cómo revertir correctamente y preservar el historial. Esta pregunta dice "Quiero que el jefe de la rama apunte a A, es decir, quiero B, C, D y HEAD para desaparecer y quiero que la cabeza sea sinónimo de A. "

git checkout <branch_name>
git reset --hard <commit Hash for A>
git push -f

Aprendí mucho leyendo la publicación de Jakub, pero un chico de la empresa (con acceso a push en nuestra rama de "pruebas" sin Pull-Request) hizo 5 commits malos tratando de arreglar y corregir un error que cometió hace 5 commits. No solo eso, sino que se aceptaron una o dos solicitudes de extracción, que ahora eran malas. Así que olvídalo, encontré el último commit bueno (abc1234) y acabo de ejecutar el script básico:

git checkout testing
git reset --hard abc1234
git push -f

Les dije a los otros 5 chicos que trabajan en este repositorio que es mejor que anoten sus cambios en las últimas horas y limpie / vuelva a ramificar de las últimas pruebas. El final de la historia.


16
2017-07-11 23:29



La forma más fácil de revertir un grupo de commits en el repositorio compartido (que las personas usan y desea preservar el historial) es usar git revert en conjunto con git rev-list. El último le proporcionará una lista de confirmaciones, el primero hará la reversión.

Hay dos formas de hacer eso. Si desea revertir varias confirmaciones en una única confirmación, use:

for i in `git rev-list <first-commit-sha>^..<last-commit-sha>`; do git revert -n $i; done

esto revertirá un grupo de confirmaciones que necesita, pero deje todos los cambios en su árbol de trabajo, debe enviarlos todos como de costumbre.

Otra opción es tener una única confirmación por cambio revertido:

for i in `git rev-list <first-commit-sha>^..<last-commit-sha>`; do git revert --no-edit -s $i; done

Por ejemplo, si tiene un árbol de commits como

 o---o---o---o---o---o--->    
fff eee ddd ccc bbb aaa

para revertir los cambios de eee a bbb, correr

for i in `git rev-list eee^..bbb`; do git revert --no-edit -s $i; done

4
2017-08-04 11:29



Esta es una expansión de una de las soluciones proporcionadas en la respuesta de Jakub

Me enfrenté a una situación en la que las confirmaciones que necesitaba revertir eran algo complejas, con varias de las confirmaciones que eran confusiones de fusión, y necesitaba evitar volver a escribir el historial. No pude usar una serie de git revert comandos porque finalmente encontré conflictos entre los cambios de reversión que se están agregando. Terminé usando los siguientes pasos.

Primero, revisa el contenido del commit del objetivo mientras dejas HEAD en la punta de la rama:

$ git checkout -f <target-commit> -- .

(El - se asegura <target-commit> se interpreta como un compromiso en lugar de un archivo; el . se refiere al directorio actual.)

Luego, determine qué archivos se agregaron en las confirmaciones que se están retrotrayendo y, por lo tanto, deben eliminarse:

$ git diff --name-status --cached <target-commit>

Los archivos que se agregaron deberían aparecer con una "A" al comienzo de la línea, y no debería haber otras diferencias. Ahora, si es necesario eliminar algunos archivos, escenifique estos archivos para su eliminación:

$ git rm <filespec>[ <filespec> ...]

Finalmente, comprometa la reversión:

$ git commit -m 'revert to <target-commit>'

Si lo desea, asegúrese de que volvemos al estado deseado:

$git diff <target-commit> <current-commit>

No debería haber diferencias.


4
2017-11-08 06:39