¿Cómo deshacer un "git cherry-pick" exitoso?

99

En un repositorio local, acabo de ejecutar git cherry-pick SHAsin ningún conflicto o problema. Entonces me di cuenta de que no quería hacer lo que acababa de hacer. No lo he empujado a ninguna parte.

¿Cómo puedo quitar solo esta selección de cereza?

Me gustaría saber si hay una forma de hacer esto:

  • cuando tengo otros cambios locales
  • cuando no tengo otros cambios locales

Preferiblemente con un comando para ambos casos si es posible.

Brad Parks
fuente

Respuestas:

135

Una selección de cerebros es básicamente una confirmación, por lo que si desea deshacerla, simplemente deshaga la confirmación.

cuando tengo otros cambios locales

Guarde sus cambios actuales para que pueda volver a aplicarlos después de restablecer la confirmación.

$ git stash
$ git reset --hard HEAD^
$ git stash pop  # or `git stash apply`, if you want to keep the changeset in the stash

cuando no tengo otros cambios locales

$ git reset --hard HEAD^
Tim
fuente
6
Solo además de la pregunta y la respuesta, si no está seguro de lo que está seleccionando, siempre puede 'elegir SHA --no-commit' y no habrá cambios de solo compromiso que se pueden verificar fácilmente , esto es útil para la selección parcial de cerezas
kuskmen
2
windows: git reset --hard "HEAD ^"
slim
22

Para deshacer su última confirmación, simplemente haga git reset --hard HEAD~.

Editar : esta respuesta se aplicó a una versión anterior de la pregunta que no mencionaba la preservación de los cambios locales; la respuesta aceptada de Tim es de hecho la correcta. Gracias a qwertzguy por el aviso.

David Deutsch
fuente
2
Eso debería ser HEAD ^ o HEAD ~ 1
Andreas Wederbrand
Ambos son equivalentes a HEAD ~ (y HEAD ^ 1, para el caso)
David Deutsch
1
@DavidDeutsch Eso es cierto (aunque solo en versiones más recientes de Git), pero mayúsculas ( HEAD) es más robusto: considere el desafortunado caso donde headestá el nombre de una referencia existente.
jub0bs
1
@Jubobs - buen punto; He cambiado la carcasa en mi respuesta.
David Deutsch
1
@qwertzguy, buen partido; Al mirar las marcas de tiempo, se agregó la parte sobre los cambios locales a la pregunta un minuto después de que publiqué esta respuesta :)
David Deutsch
8

Si es posible, evite los reinicios bruscos. Los reinicios duros son una de las pocas operaciones destructivas en git. Afortunadamente, puedes deshacer una selección sin reiniciar y evitar cualquier cosa destructiva.

Tenga en cuenta el hash de la selección que desea deshacer, digamos que lo es ${bad_cherrypick}. Hacer ungit revert ${bad_cherrypick} . Ahora el contenido de su árbol de trabajo es como estaba antes de su mala elección.

Repita su git cherry-pick ${wanted_commit}, y cuando esté satisfecho con la nueva selección de cereza, haga un git rebase -i ${bad_cherrypick}~1. Durante el rebase, elimine ambos${bad_cherrypick} y su correspondiente reversión.

La rama en la que está trabajando solo tendrá la mejor selección. ¡No se necesitan reinicios!

Cuadue
fuente
6

git reflog puede venir a tu rescate.

Escríbalo en su consola y obtendrá una lista de su historial de git junto con SHA-1 que los representa.

Simplemente revise cualquier SHA-1 al que desee volver


Antes de responder, agreguemos algunos antecedentes, explicando qué es esto HEAD.

First of all what is HEAD?

HEADes simplemente una referencia a la confirmación actual (más reciente) en la rama actual.
Solo puede haber uno HEADen un momento dado. (excluyendo git worktree)

El contenido de HEADse almacena adentro .git/HEADy contiene los 40 bytes SHA-1 de la confirmación actual.


detached HEAD

Si no está en la última confirmación, lo que significa que HEADapunta a una confirmación anterior en el historial, se llama detached HEAD.

ingrese la descripción de la imagen aquí

En la línea de comando, se verá así: SHA-1 en lugar del nombre de la rama, ya HEADque no apunta a la punta de la rama actual

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

Algunas opciones sobre cómo recuperarse de un HEAD desprendido:


git checkout

git checkout <commit_id>
git checkout -b <new branch> <commit_id>
git checkout HEAD~X // x is the number of commits t go back

Esto comprobará una nueva rama que apunta a la confirmación deseada.
Este comando se ejecutará en una confirmación determinada.
En este punto, puede crear una rama y comenzar a trabajar a partir de este punto.

# Checkout a given commit. 
# Doing so will result in a `detached HEAD` which mean that the `HEAD`
# is not pointing to the latest so you will need to checkout branch
# in order to be able to update the code.
git checkout <commit-id>

# create a new branch forked to the given commit
git checkout -b <branch name>

git reflog

Siempre puedes usar el reflogtambién.
git reflogmostrará cualquier cambio que haya actualizado HEADy comprobar la entrada de reflog deseada volverá a establecer HEADesta confirmación.

Cada vez que se modifica HEAD habrá una nueva entrada en el reflog

git reflog
git checkout HEAD@{...}

Esto te llevará de regreso a tu compromiso deseado

ingrese la descripción de la imagen aquí


git reset --hard <commit_id>

"Mueve" tu HEAD de nuevo a la confirmación deseada.

# This will destroy any local modifications.
# Don't do it if you have uncommitted work you want to keep.
git reset --hard 0d1d7fc32

# Alternatively, if there's work to keep:
git stash
git reset --hard 0d1d7fc32
git stash pop
# This saves the modifications, then reapplies that patch after resetting.
# You could get merge conflicts if you've modified things which were
# changed since the commit you reset to.
  • Nota: ( Desde Git 2.7 )
    también puedes usar el git rebase --no-autostash.

git revert <sha-1>

"Deshacer" el compromiso dado o el rango de compromiso.
El comando de reinicio "deshará" cualquier cambio realizado en la confirmación dada.
Se confirmará una nueva confirmación con el parche para deshacer, mientras que la confirmación original también permanecerá en el historial.

# add new commit with the undo of the original one.
# the <sha-1> can be any commit(s) or commit range
git revert <sha-1>

Este esquema ilustra qué comando hace qué.
Como puede ver, reset && checkoutmodifique el HEAD.

ingrese la descripción de la imagen aquí

CodeWizard
fuente
quizás agregargit reset --hard
wyx
3

Enfrentado con este mismo problema, descubrí que si se ha comprometido y / o empujado al control remoto desde su selección de cereza exitosa, y desea eliminarlo, puede encontrar el SHA de la selección de cereza ejecutando:

git log --graph --decorate --oneline

Luego, (después de usar :wqpara salir del registro) puede eliminar la selección de cereza usando

git rebase -p --onto YOUR_SHA_HERE^ YOUR_SHA_HERE

donde YOUR_SHA_HEREes igual al SHA de 40 o 7 caracteres abreviado de la confirmación seleccionada con precisión.

Al principio, no podrá impulsar sus cambios porque su repositorio remoto y su repositorio local tendrán diferentes historiales de confirmación. Puede forzar a sus confirmaciones locales a reemplazar lo que está en su control remoto usando

git push --force origin YOUR_REPO_NAME

(Adapté esta solución de Seth Robertson : consulte "Eliminación de una confirmación completa").

brogrammer
fuente
1

Un comando y no usa el git resetcomando destructivo :

GIT_SEQUENCE_EDITOR="sed -i 's/pick/d/'" git rebase -i HEAD~ --autostash

Simplemente elimina la confirmación, lo que lo vuelve a colocar exactamente en el estado anterior a la selección, incluso si tenía cambios locales.

qwertzguy
fuente
Simplemente elimina el compromiso. que crees que git reset HEAD^hace
Tim
@TimCastelijns git resetdeshace la acción de confirmación sin eliminar ninguno de los cambios. Te recomiendo leer git-scm.com/docs/git-reset . La pregunta de OP es deshacer una selección selectiva. Pruebe ambos comandos, el mío lo pondrá de nuevo en el estado exacto en el que estaba antes de la selección. git resetarruinará su árbol de trabajo cuando deje los cambios seleccionados y se volverán indistinguibles de sus cambios locales preexistentes.
qwertzguy
sí, lo siento, quise decir reset --hard. No deja cambios
Tim
1
@TimCastelijns git reset --hardes un comando destructivo que también eliminará los cambios locales no relacionados y lo hará de una manera irrecuperable. Entonces eso no es lo que pidió el OP.
qwertzguy
2
Esta respuesta sería mucho más útil si explicaras qué sedestá haciendo ese comando, en qué se diferencia de hacerlo manualmente y qué --autostashhace.
Chris Page