Deshacer un git rebase

3180

¿Cómo puedo deshacer fácilmente un git rebase?

Mis ideas actuales son solo enfoques manuales:

  1. git checkout en el padre de confirmación a ambas ramas
  2. Crea una rama temporal desde allí
  3. git cherry-pick todos los commits a mano
  4. reemplace la rama en la que rebase por la rama creada manualmente

En mi situación actual, esto funcionaría porque puedo detectar fácilmente las confirmaciones de ambas ramas (una era lo mío, la otra era lo de mi colega).

Sin embargo, mi enfoque me parece subóptimo y propenso a errores (digamos que acababa de cambiar el nombre con 2 de mis propias ramas).

Aclaración : estoy hablando de un rebase durante el cual se repitieron varios commits. No solo uno.

webmat
fuente
66
TAMBIÉN tenga en cuenta que durante un rebase puede excluir commits o aplastarlos; estos cambios no son revertables sin un puntero a los nodos originales o sin examinar el registro, por lo que la selección no funcionaría.
ANeves

Respuestas:

4337

La forma más fácil sería encontrar el commit principal de la sucursal tal como estaba inmediatamente antes de que comenzara el rebase en el registro ...

git reflog

y para restablecer la rama actual (con las advertencias habituales sobre estar absolutamente seguro antes de restablecer con la --hardopción).

Supongamos que la confirmación anterior estaba HEAD@{5}en el registro de referencia:

git reset --hard HEAD@{5}

En Windows, puede que necesite citar la referencia:

git reset --hard "HEAD@{5}"

Puede verificar el historial del antiguo jefe candidato simplemente haciendo un git log HEAD@{5}( Windows:) git log "HEAD@{5}" .

Si no ha deshabilitado los registros de registros por rama, debería poder hacerlo simplemente ya git reflog branchname@{1}que un rebase separa la cabeza de la rama antes de volver a unirla a la cabeza final. Verificaría esto dos veces, aunque no lo he verificado recientemente.

Por defecto, todos los reflogs se activan para repositorios no desnudos:

[core]
    logAllRefUpdates = true
CB Bailey
fuente
115
El reflog de Git es increíble, solo recuerda que puedes obtener una salida mejor formateada con git log -g(consejo de progit.org/book de Scott Chacon ).
karmi
6060
@Zach: git rebase --abort( -ino tiene sentido --abort) es por abandonar un rebase que no se ha completado, ya sea porque hubo conflictos o porque fue interactivo o ambos; no se trata de deshacer un rebase exitoso, de eso se trata la pregunta. Usted usaría rebase --aborto reset --harddependiendo de la situación en la que se encuentre. No debería necesitar hacer ambas cosas.
CB Bailey
311
Por si acaso, hacer una copia de seguridad primero: git tag BACKUP. Puede regresar si algo sale mal:git reset --hard BACKUP
kolypto
66
Si ha realizado muchas confirmaciones, el HEAD @ {#} que busca estará precedido commit:en lugar de rebase:. Suena obvio pero me confundió un poco.
Warpling
44
Unirse a la fiesta después de un rebase accidental: D. ¿No git reset --hard ORIG_HEADharía el truco tan bien inmediatamente después del rebase accidental?
Quaylar
1488

En realidad, rebase guarda su punto de partida, por ORIG_HEADlo que esto suele ser tan simple como:

git reset --hard ORIG_HEAD

Sin embargo, el reset, rebasey mergetodo Guardar el original HEADpuntero en ORIG_HEADlo que, si usted ha hecho alguna de esas órdenes ya que el rebase que está tratando de deshacer entonces usted tendrá que usar la reflog.

Pat Notz
fuente
34
En caso de ORIG_HEADque ya no sea útil, también puede usar la branchName@{n}sintaxis, donde nes la enésima posición anterior del puntero de rama. Entonces, por ejemplo, si rebase la featureArama en su masterrama, pero no le gusta el resultado de la rebase, entonces simplemente puede hacer git reset --hard featureA@{1}para restablecer la rama exactamente donde estaba antes de hacer la rebase. Puede leer más sobre la sintaxis de branch @ {n} en los documentos oficiales de Git para revisiones .
15
Este es el más fácil. Sigue con un git rebase --abortpensamiento.
Seph
1
@DaBlick esto funcionó bien para mí después de un rebase completamente exitoso sin conflictos git 2.17.0.
dvlsg el
44
Y permítanme complementarlo: git reset --hard ORIG_HEADpuede usarlo repetidamente para retroceder una y otra vez. Digamos, si A --- rebase a --- B --- rebase a --- C, ahora estoy en C, puedo volver a A usando dos vecesgit reset --hard ORIG_HEAD
CalvinChe
55
@Seph ¿Puedes explicar por qué sugieres hacer un seguimiento git rebase --abort?
UpTheCreek
386

La respuesta de Charles funciona, pero es posible que desee hacer esto:

git rebase --abort

para limpiar después de la reset.

De lo contrario, puede recibir el mensaje " Interactive rebase already started".

Alano
fuente
44
Este eliminó la parte "| REBASE" en mi mensaje. +1
Wouter Thielen
6262
Esa no era la pregunta. La pregunta pregunta cómo deshacer un rebase terminado.
Arunav Sanyal
2
Debería haber un comentario sobre la respuesta de Charles, porque no responde a la pregunta en su encendido
Murmel
Hm, no tuve que hacer eso.
Viktor Seč
1
¡El único que funcionó para mí!
Alexandre Picard
90

Restablecer la rama al objeto de confirmación colgante de su punta anterior es, por supuesto, la mejor solución, porque restaura el estado anterior sin gastar ningún esfuerzo. Pero si por casualidad ha perdido esas confirmaciones (por ejemplo, porque recolectó basura su repositorio mientras tanto, o este es un clon nuevo), siempre puede volver a cambiar la rama. La clave para esto es el --ontointerruptor.

Digamos que tenía una rama temática llamada imaginativamente topic, que se ramificó mastercuando la punta de masterla 0deadbeefconfirmación era. En algún momento mientras topicestabas en la rama, lo hiciste git rebase master. Ahora quieres deshacer esto. Así es cómo:

git rebase --onto 0deadbeef master topic

Esto tomará todas las confirmaciones topicque no estén masteractivadas y las reproducirá en la parte superior 0deadbeef.

Con --onto, puedes reorganizar tu historia en casi cualquier forma .

Que te diviertas. :-)

Aristóteles Pagaltzis
fuente
3
Creo que esta es la mejor opción debido a su flexibilidad. Bramé b1 fuera del maestro, luego volví a formar b1 en una nueva rama b2, luego quise revertir b1 para basarme en el maestro nuevamente. Me encanta git, ¡gracias!
ripper234
2
¡Esta es la mejor opción aquí! ¡Mantuvo todos los cambios que tengo en mi rama actual y eliminó todos los no deseados!
Alicia Tang
Yo diría que con una combinación de --ontoy -ipuedes reorganizar tu historia en casi cualquier forma. Usa gitk (o gitx en mac) para ver las formas que haces :-).
rjmunro
69

De hecho, puse una etiqueta de copia de seguridad en la rama antes de hacer cualquier operación no trivial (la mayoría de las rebase son triviales, pero lo haría si se ve en algún lugar complejo).

Entonces, restaurar es tan fácil como git reset --hard BACKUP.

Alex Gontmakher
fuente
2
Yo también hago esto. También es útil usar git diff BACKUP..HEAD para asegurarte de que cambiaste lo que querías.
Paul Bone
55
Yo solía hacer eso también, pero como me sentí más cómodo con el reflog ya no siento que sea necesario. El reflog es esencialmente hacer esto en su nombre cada vez que cambia HEAD.
Pete Hodgson el
44
Bueno, prefiero nombres significativos, porque buscar el elemento correcto en el registro a veces no es divertido en absoluto.
Alex Gontmakher
12
En realidad, ni siquiera necesita hacer una rama de respaldo, simplemente puede usar la branchName@{n}sintaxis, aquí nestá la enésima posición anterior del puntero de rama. Entonces, por ejemplo, si vuelve a crear una featureArama en su masterrama, pero no le gusta el resultado de la modificación, entonces simplemente puede hacer git reset --hard featureA@{1}para restablecer la rama exactamente donde estaba antes de hacer la modificación. Puede leer más sobre la branch@{n}sintaxis en los documentos oficiales de Git para revisiones .
2
En realidad, ni siquiera necesita usar la sintaxis anterior, de acuerdo con la respuesta de Pat Notz , el original HEADde la rama se almacena temporalmente en ORIG_HEAD. Pero la técnica de usar una etiqueta de rama de respaldo también funciona, son solo más pasos.
68

En caso de que haya empujado su bifurcación al repositorio remoto (generalmente es el origen) y luego haya realizado un cambio de base exitoso (sin fusión) ( git rebase --abortda "No hay rebase en progreso"), puede restablecer fácilmente la rama usando el comando:

git reset --hard source / {branchName}

Ejemplo:

$ ~/work/projects/{ProjectName} $ git status
On branch {branchName}
Your branch is ahead of 'origin/{branchName}' by 135 commits.
  (use "git push" to publish your local commits)

nothing to commit, working directory clean

$ ~/work/projects/{ProjectName} $ git reset --hard origin/{branchName}
HEAD is now at 6df5719 "Commit message".

$ ~/work/projects/{ProjectName} $ git status
On branch {branchName}
Your branch is up-to-date with 'origin/{branchName}.

nothing to commit, working directory clean
Maksym
fuente
Esa es la respuesta correcta para mí. Rebase y confirme antes del rebase tenía la misma ID de confirmación, y volver a HEAD {1} simplemente no revertiría el rebase.
Bill Kotsias
23

Usar reflogno funcionó para mí.

Lo que funcionó para mí fue similar a lo que se describe aquí . Abra el archivo en .git / logs / refs con el nombre de la rama que se ha modificado y busque la línea que contiene "rebase finsihed", algo así como:

5fce6b51 88552c8f Kris Leech <[email protected]> 1329744625 +0000  rebase finished: refs/heads/integrate onto 9e460878

Verifique el segundo commit listado en la línea.

git checkout 88552c8f

Una vez confirmado que contenía mis cambios perdidos, me bifurqué y solté un suspiro de alivio.

git log
git checkout -b lost_changes
Kris
fuente
3
Whoa - desde ese enlace, "Hay una advertencia: perdí el historial de la rama, pero en este caso realmente no importó. Estaba feliz de haber recuperado mis cambios". ?
ruffin
16

Para las confirmaciones múltiples, recuerde que cualquier confirmación hace referencia a toda la historia previa a esa confirmación. Entonces, en la respuesta de Charles, lea "el viejo commit" como "el más nuevo de los viejos commits". Si restablece esa confirmación, volverá a aparecer todo el historial previo a esa confirmación. Esto debería hacer lo que quieras.

Greg Hewgill
fuente
11

Siguiendo la solución de @Allan y @Zearin, desearía poder simplemente hacer un comentario, pero no tengo suficiente reputación, así que he usado el siguiente comando:

En lugar de hacerlo git rebase -i --abort (tenga en cuenta el -i ) tuve que hacer simplemente git rebase --abort( sin el -i ).

Usar ambos -iy --abortal mismo tiempo hace que Git me muestre una lista de uso / opciones.

Entonces, mi estado de sucursal anterior y actual con esta solución es:

matbhz@myPc /my/project/environment (branch-123|REBASE-i)
$ git rebase --abort

matbhz@myPc /my/project/environment (branch-123)
$
Matheus Felipe
fuente
11

Si reescribió con éxito contra una rama remota y no puede git rebase --abort, aún puede hacer algunos trucos para salvar su trabajo y no tener empujes forzados. Supongamos que se llama a su rama actual que se modificó por error your-branchy está rastreandoorigin/your-branch

  • git branch -m your-branch-rebased # cambiar el nombre de la rama actual
  • git checkout origin/your-branch # checkout al último estado que se conoce como origen
  • git checkout -b your-branch
  • verificar git log your-branch-rebased, comparar git log your-branchy definir confirmaciones que faltan enyour-branch
  • git cherry-pick COMMIT_HASH por cada compromiso en your-branch-rebased
  • empuja tus cambios. Tenga en cuenta que hay dos sucursales locales asociadas remote/your-branchy debe presionar soloyour-branch
Sergey P. alias azure
fuente
4

Digamos que rebase master a mi rama de características y obtengo 30 nuevos commits que rompen algo. He descubierto que a menudo es más fácil eliminar las confirmaciones incorrectas.

git rebase -i HEAD~31

Reorganización interactiva para los últimos 31 commits (no hace daño si elige demasiados).

Simplemente tome los commits de los que desea deshacerse y márquelos con "d" en lugar de "pick". Ahora los commits se eliminan efectivamente deshaciendo el rebase (si elimina solo los commits que acaba de obtener al rebase).

Hardev
fuente
3

Para los novatos / alguien demasiado asustado de hacer un restablecimiento completo, puede verificar el compromiso desde el registro y luego guardarlo como una nueva rama.

git reflog

Encuentra el commit justo antes de comenzar a rebasar. Es posible que deba desplazarse más hacia abajo para encontrarlo (presione Entrar o PageDown). Tome nota del número de HEAD y reemplace 57:

git checkout HEAD@{57}

Revise la rama / commits, si se ve bien, cree una nueva rama usando este HEAD:

git checkout -b new_branch_name
Andrés
fuente
2

Si estás en una sucursal puedes usar:

git reset --hard @{1}

No solo hay un registro de referencia para HEAD (obtenido por git reflog), también hay reflogs para cada rama (obtenida por git reflog <branch>). Entonces, si está masteractivado git reflog master, enumerará todos los cambios en esa rama. Se puede hacer referencia a que los cambios de master@{1}, master@{2}, etc.

git rebase por lo general, cambiará HEAD varias veces, pero la rama actual se actualizará solo una vez.

@{1}es simplemente un acceso directo para la rama actual , por lo que es igual a master@{1}si está activado master.

git reset --hard ORIG_HEADno funcionará si lo usó git resetdurante un interactivo rebase.

devconsole
fuente
1

Lo que suelo hacer es git reset #commit_hash

hasta el último commit donde creo que rebase no tuvo efecto.

entonces git pull

Ahora su rama debe coincidir exactamente como las confirmaciones maestra y con rebase no deben estar en ella.

Ahora uno solo puede elegir los commits en esta rama.

mrigendra
fuente
1

Intenté todas las sugerencias con restablecer y volver a registrar sin ningún éxito. Restaurar el historial local de IntelliJ resolvió el problema de los archivos perdidos

usuario3638751
fuente
¡Gracias! Nunca utilicé el historial local antes, pero resultó ser la opción más fácil para mí para recuperar un código modificado accidentalmente. Característica muy agradable pero algo oculta.
Magnus W
0

git reset --hard source / {branchName}

es la solución correcta para restablecer todos los cambios locales realizados por rebase.

Damodar P
fuente
1
si restablece por completo origin/branch, puede perder los cambios entre HEAD y ese punto. No quieres eso en general.
bluesmonk
Esto es exactamente lo que quería en mi caso. Así que votando.
Mandar Vaze
-4

Si desordena algo dentro de un rebase git, por ejemplo git rebase --abort, mientras tiene archivos no confirmados, se perderán y git reflogno ayudarán. Esto me sucedió y tendrás que pensar fuera de la caja aquí. Si tiene suerte como yo y utiliza IntelliJ Webstorm, right-click->local historypuede volver al estado anterior de sus archivos / carpetas sin importar los errores que haya cometido con el software de control de versiones. Siempre es bueno tener otro funcionamiento a prueba de fallos.

stevek
fuente
55
git rebase --abortaborta un rebase activo, no deshace un rebase. Además, usar dos VCS al mismo tiempo es una mala idea. Es una buena característica en el software Jetbrains, pero no debe usar ambos. Es mejor aprender Git, especialmente al responder preguntas sobre Stack Overflow que tratan sobre Git.
dudewad