Deshacer una rebase de git
-
02-07-2019 - |
Pregunta
¿Alguien sabe cómo deshacer fácilmente una rebase de git?
La única forma que se me ocurre es hacerlo manualmente:
- git checkout el padre de compromiso para ambas ramas
- luego crea una rama temporal desde allí
- seleccione todas las confirmaciones a mano
- reemplazar la rama en la que volví a basar por la rama creada manualmente
En mi situación actual, esto funcionará porque puedo detectar fácilmente las confirmaciones de ambas ramas (una era mía, la otra era de mi colega).
Sin embargo, mi enfoque me parece subóptimo y propenso a errores (digamos que acababa de cambiar la base con 2 de mis propias ramas).
¿Algunas ideas?
Aclaración:Estoy hablando de una rebase durante la cual se repitieron un montón de confirmaciones.No solo uno.
Solución
La forma más fácil sería encontrar el commit principal de la rama tal como estaba inmediatamente antes de que comenzara el rebase en reflog ...
git reflog
y para restablecer la rama actual (con las advertencias habituales sobre estar absolutamente seguro antes de restablecer con la opción --hard
).
Suponga 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 simplemente hacer git reflog branchname@{1}
como una nueva separación separa la cabeza de la rama antes de volver a colocarla en la cabeza final. Verificaría esto dos veces, aunque no lo he verificado recientemente.
Por defecto, todos los reflogs se activan para repositorios no descubiertos:
[core]
logAllRefUpdates = true
Otros consejos
En realidad, rebase guarda su punto de partida en ORIG_HEAD
entonces esto suele ser tan simple como:
git reset --hard ORIG_HEAD
sin embargo, el reset
, rebase
y merge
todos guardan su original HEAD
puntero hacia ORIG_HEAD
entonces, si ha ejecutado alguno de esos comandos desde el cambio de base que está intentando deshacer, tendrá que usar reflog.
La respuesta de Charles funciona, pero es posible que desee hacer esto:
git rebase --abort
para limpiar después del reset
.
De lo contrario, puede recibir el mensaje & # 8220; Interactive rebase already started
& # 8221 ;.
git reflog
le mostrará todos los cambios antes y después del rebase, y le permitirá encontrar el correcto para restablecer. Pero me sorprende que nadie haya mencionado esta otra forma súper simple aquí todavía:
Rebase deja el estado anterior como ORIG_HEAD
, para que pueda revertir el último rebase ejecutando:
git reset --hard ORIG_HEAD
Restablecer la rama al objeto de confirmación pendiente de su antiguo tip es, por supuesto, la mejor solución, porque restaura el estado anterior sin gastar ningún esfuerzo.Pero si ha perdido esas confirmaciones (p.ej.Debido a que mientras tanto recogiste basura en tu repositorio, o este es un clon nuevo), siempre puedes cambiar la base de la rama nuevamente.La clave para esto es la --onto
cambiar.
Digamos que tienes una rama temática llamada imaginativamente topic
, que te bifurcaste master
cuando la punta de master
fue el 0deadbeef
comprometerse.En algún momento mientras estaba en el topic
rama, lo hiciste git rebase master
.Ahora quieres deshacer esto.Así es cómo:
git rebase --onto 0deadbeef master topic
Esto requerirá todos los compromisos topic
que no estan encendidos master
y reproducirlos encima de 0deadbeef
.
Con --onto
, puedes reorganizar tu historial en prácticamente cualquier forma.
Divertirse.:-)
De hecho, puse una etiqueta de respaldo en la rama antes de hacer una operación no trivial (la mayoría de las rebase son triviales, pero lo haría si se ve en algún lugar complejo).
Luego, la restauración es tan fácil como git reset --hard BACKUP
.
En caso de que haya empujado su sucursal al repositorio remoto (generalmente es el origen) y luego haya realizado un rebase exitoso (sin fusión) (git rebase --abort
da " Sin rebase en progreso ") puede fácilmente restablecer rama usando
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
En caso de que no haya completado el rebase y en medio de él, lo siguiente funciona:
git rebase --abort
Usar reflog
no funcionó para mí.
Lo que funcionó para mí fue similar a lo descrito aquí . Abra el archivo en .git / logs / refs con el nombre de la rama que se ha modificado y encuentre la línea que contiene & Quot; rebase finsihed & Quot ;, algo así como:
5fce6b51 88552c8f Kris Leech <me@example.com> 1329744625 +0000 rebase finished: refs/heads/integrate onto 9e460878
Revisa 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
Para las confirmaciones múltiples, recuerde que cualquier confirmación hace referencia a todo el historial previo a esa confirmación. Entonces, en la respuesta de Charles, lea & Quot; el antiguo commit & Quot; 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.
Siguiendo la solución de @Allan y @Zearin, me gustaría poder simplemente hacer un comentario, pero no tengo suficiente reputación, así que utilicé el siguiente comando:
en lugar de hacer git rebase -i --abort
(nota la -i) Tuve que simplemente hacer git rebase --abort
(sin el -i).
Usando ambos -i
y --abort
al mismo tiempo hace que Git me muestre una lista de uso/opciones.
Entonces, el estado de mi 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)
$
Si rebasó exitosamente la sucursal remota y no puede git rebase --abort
Aún puedes hacer algunos trucos para guardar tu trabajo y no tener que forzarlo.Supongamos que su rama actual que fue rebasada por error se llama your-branch
y está rastreando origin/your-branch
git branch -m your-branch-rebased
# cambiar el nombre de la rama actualgit checkout origin/your-branch
# pago al último estado conocido por el origengit checkout -b your-branch
- controlar
git log your-branch-rebased
, comparar congit log your-branch
y definir confirmaciones que faltan enyour-branch
git cherry-pick COMMIT_HASH
por cada compromiso enyour-branch-rebased
- impulsar sus cambios.Tenga en cuenta que dos sucursales locales están asociadas con
remote/your-branch
y solo debes empujaryour-branch
Digamos que reescribo el master a mi rama de características y obtengo 30 confirmaciones nuevas 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 las últimas 31 confirmaciones (no hace daño si elige demasiadas).
Simplemente tome los commits de los que desea deshacerse y márquelos con " d " en lugar de " seleccione " ;. Ahora, los commits se eliminan efectivamente deshaciendo el rebase (si elimina solo los commits que acaba de obtener al rebase).
Si está en una sucursal, puede 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á en master
entonces git reflog master
enumerará todos los cambios en esa rama. Puede referirse a esos cambios por master@{1}
, master@{2}
, etc.
git rebase
generalmente cambiará HEAD varias veces, pero la rama actual se actualizará solo una vez.
@{1}
es simplemente un acceso directo para la rama actual , entonces es igual a git reset --hard ORIG_HEAD
si está en git reset
.
rebase
no funcionará si usó <=> durante un <=> interactivo.
Para los novatos / cualquiera que esté demasiado asustado de hacer un restablecimiento completo, puede verificar el commit desde el registro y luego guardarlo como una nueva rama.
git reflog
Encuentre la confirmación justo antes de comenzar a cambiar el nombre. Es posible que deba desplazarse más hacia abajo para encontrarlo (presione Entrar o PageDown). Tome nota del número 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
git reset --hard source / {branchName}
es la solución correcta para restablecer todos los cambios locales realizados por rebase.
Lo que suelo hacer es
git reset #commit_hash
a la última confirmación donde creo que rebase no tuvo efecto.
luego git pull
Ahora su rama debería coincidir exactamente como las confirmaciones master y rebase no deberían estar en ella.
Ahora uno puede simplemente seleccionar los commits en esta rama.
Si arruinas algo dentro de una rebase de git, p. git rebase --abort
, mientras tenga archivos no confirmados, se perderán y git reflog
no ayudará.Esto me pasó a mí y tendrás que pensar fuera de lo común aquí.Si tienes suerte como yo y usas IntelliJ Webstorm, entonces puedes right-click->local history
y puede volver a un 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 sistema de seguridad en ejecución.