¿Puedo eliminar un git commit pero mantener los cambios?

1057

En una de mis ramas de desarrollo, hice algunos cambios en mi código base. Antes de poder completar las funciones en las que estaba trabajando, tuve que cambiar mi rama actual a maestra para demostrar algunas funciones. Pero solo usando un "git checkout master" conservó los cambios que también hice en mi rama de desarrollo, rompiendo así algunas de las funcionalidades en master. Entonces, lo que hice fue confirmar los cambios en mi rama de desarrollo con un mensaje de confirmación "confirmación temporal" y luego finalizar la compra para la demostración.

Ahora que terminé con la demostración y volví a trabajar en mi rama de desarrollo, me gustaría eliminar el "compromiso temporal" que hice sin dejar de preservar los cambios que hice. ¿Es eso posible?

tanookiben
fuente
65
La próxima vez:git stash
Matt Ball
51
@MattBall, no necesariamente. Si bien git stashes una buena herramienta, los commits de "trabajo en progreso" son también un dispositivo legítimo.
kostix
3
Este es un gran estrecho de recursos de Github: Cómo deshacer (casi) cualquier cosa con Git
jasonleonhard 03 de
99
@MattBall @kostix Sí, el alijo es particularmente inadecuado para el escondite "a largo plazo", dado que es una pila global para todo el repositorio. Quiero poder esconder los cambios en una rama y luego ir a otras ramas, hacer cualquier otra cosa, volver días después sin preocuparme de que podría haber usado git stashalguna otra rama en el ínterin.
Alec
44
Una cosa que vale la pena señalar stashes que es completamente local y será propenso a la pérdida de código por eliminación de datos o recreación o, falla o pérdida de hardware. OMI, realmente debería usarse solo para WIP a muy corto plazo. Me encanta un compromiso de WIP antes de irse de vacaciones: P .. ¡llámalo un compromiso de descarga de cerebro!
ϹοδεMεδιϲ

Respuestas:

1620

Es tan simple como esto:

git reset HEAD^

Nota: algunos shells se tratan ^como un carácter especial (por ejemplo, algunos shells de Windows o ZSH con globing habilitado ), por lo que es posible que tenga que citar "HEAD^"en esos casos.

git resetsin a --hardo --softmueve su HEADpara apuntar a la confirmación especificada, sin cambiar ningún archivo. HEAD^se refiere a la (primera) confirmación principal de su confirmación actual, que en su caso es la confirmación anterior a la temporal.

Tenga en cuenta que otra opción es continuar con normalidad y luego, en el siguiente punto de confirmación, ejecutar:

git commit --amend [-m … etc]

que en su lugar editará la confirmación más reciente, teniendo el mismo efecto que el anterior.

Tenga en cuenta que esto (como con casi todas las respuestas de git) puede causar problemas si ya ha empujado la mala confirmación a un lugar donde otra persona puede haberla retirado. Intenta evitar eso

Gareth
fuente
47
Este es, con mucho, el más simple, pero si ya ha empujado su confirmación a un control remoto y alguien más lo retiró, dudaría mucho en hacer cualquier cosa excepto disculparme.
atw13
11
@sicophrenic, no pierdas la oportunidad de leer "Reset Demystified" en esta ocasión.
kostix
33
Me sale More?después de hacer esto. Cualquier cosa que escriba en ese mensaje me dafatal: ambiguous argument 'HEADwhateverItypedIn': unknown revision or path not in the working tree.
DaAwesomeP
50
@DaAwesomeP suena como si estuvieras usando un shell que trata ^como un personaje especial. Puede citar la referencia "HEAD^"o utilizar la sintaxis alternativa sin HEAD~1comillas
Gareth,
8
Trabajado para mí, tenía que escapar de carácter embargogit reset HEAD\^
cevaris
189

Hay dos formas de manejar esto. Lo que es más fácil depende de tu situación.

Reiniciar

Si la confirmación de la que desea deshacerse fue la última confirmación, y no ha realizado ningún trabajo adicional, simplemente puede usar git-reset

git reset HEAD^

Lleva su rama nuevamente al commit justo antes de su HEAD actual. Sin embargo, en realidad no cambia los archivos en su árbol de trabajo. Como resultado, los cambios que estaban en esa confirmación aparecen como modificados, es como un comando 'no confirmado'. De hecho, tengo un alias para hacer precisamente eso.

git config --global alias.uncommit 'reset HEAD^'

Entonces puede usarlo git uncommiten el futuro para hacer una copia de seguridad de una confirmación.

Aplastamiento

Aplastar un commit significa combinar dos o más commits en uno. Hago esto con bastante frecuencia. En su caso, tiene una característica a medio hacer confirmada, y luego la finalizaría y volvería a confirmar con el mensaje de confirmación permanente adecuado.

git rebase -i <ref>

Digo arriba porque quiero dejar en claro que esto podría ser cualquier cantidad de confirmaciones. Ejecute git logy encuentre el commit del que desea deshacerse, copie su SHA1 y úselo en lugar de <ref>. Git te llevará al modo de rebase interactivo. Mostrará todas las confirmaciones entre su estado actual y cualquier cosa que ponga en su lugar <ref>. Entonces, si <ref>hace 10 commits, te mostrará los 10 commits.

Delante de cada commit, tendrá la palabra pick. Encuentra el commit desea deshacerse de y cambiarlo de picka fixupo squash. Usar fixupsimplemente descartes que confirman el mensaje y fusionan los cambios en su predecesor inmediato en la lista. La squashpalabra clave hace lo mismo, pero le permite editar el mensaje de confirmación de la nueva confirmación combinada.

Tenga en cuenta que las confirmaciones se volverán a confirmar en el orden en que aparecen en la lista cuando salga del editor. Entonces, si realizó una confirmación temporal, luego realizó otro trabajo en la misma rama y completó la función en una confirmación posterior, entonces el uso de rebase le permitiría volver a ordenar las confirmaciones y aplastarlas.

ADVERTENCIA:

Rebasar modifica el historial: NO haga esto con ninguna confirmación que ya haya compartido con otros desarrolladores.

Escondite

En el futuro, para evitar este problema, considere usarlo git stashpara almacenar temporalmente el trabajo no comprometido.

git stash save 'some message'

Esto almacenará tus cambios actuales a un lado en tu lista de escondite. Arriba está la versión más explícita del comando stash, que permite un comentario para describir lo que está ocultando. También puede simplemente ejecutar git stashy nada más, pero no se almacenará ningún mensaje.

Puede navegar por su lista de alijo con ...

git stash list

Esto le mostrará todas sus reservas, en qué ramas se hicieron, y el mensaje y al comienzo de cada línea, e identificador para esa reserva que se ve así stash@{#}donde # es su posición en la matriz de reservas.

Para restaurar un alijo (que se puede hacer en cualquier rama, independientemente de dónde se creó originalmente el alijo) simplemente ejecute ...

git stash apply stash@{#}

Una vez más, # es la posición en la matriz de escondites. Si el alijo que desea restaurar está en la 0posición, es decir, si era el alijo más reciente. A continuación, sólo puede ejecutar el comando sin especificar la posición de escondite, git asumirá que quiere decir la última: git stash apply.

Entonces, por ejemplo, si me encuentro trabajando en la rama incorrecta, puedo ejecutar la siguiente secuencia de comandos.

git stash
git checkout <correct_branch>
git stash apply

En su caso, se movió un poco más por las ramas, pero la misma idea aún se aplica.

Espero que esto ayude.

eddiemoya
fuente
66
git config --global alias.uncommit reset HEAD^solo los alias no confirman para restablecer. En su lugar, hacergit config --global alias.uncommit 'reset HEAD^'
mernst
3
Tenga en cuenta que tendrá que usar ^^ en lugar de ^ si lo usa en un símbolo del sistema de Windows.
Pramod BR
106

Creo que estas buscando esto

git reset --soft HEAD~1

Deshace la confirmación más reciente mientras mantiene los cambios realizados en esa confirmación a la puesta en escena.

Sudhanshu Jain
fuente
77
Gracias. Esto funcionó para mí. Llamar git reset HEAD^a Windows solo te pregunta "¿Más?" - lo que sea que eso signifique
Tyron
12
@Tyron ^es un personaje de escape en DOS. Cuando se combina con una nueva línea, sirve como un mensaje de continuación para el comando anterior. Escribir git reset HEAD^^debería funcionar en Windows.
trk
40

Sí, puede eliminar su confirmación sin eliminar los cambios: git reset @ ~

DURGESH
fuente
77
Esto es realmente lo que quiero, y será la respuesta aceptada en mi opinión, ¡muchas gracias!
Carlos Liu
2
Interesante, sintaxis compacta, no la he visto ni la he usado antes. ¿Cómo es eso diferente de git reset --softo git reset --keep?
user776686
18

Estás buscando uno git reset HEAD^ --softu otro git reset HEAD^ --mixed.

Hay 3 modos para el comando de reinicio como se indica en los documentos :

git reset HEAD^ --soft

deshacer el git commit. Todavía existen cambios en el árbol de trabajo (la carpeta del proyecto) + el índice (--cached)

git reset HEAD^ --mixed

deshacer git commit+ git add. Todavía existen cambios en el árbol de trabajo.

git reset HEAD^ --hard

Como si nunca hubieras hecho estos cambios en la base de código. Los cambios se han ido del árbol de trabajo.

Bar Horing
fuente
9

Para aquellos que usan zsh, deberán usar lo siguiente:

git reset --soft HEAD\^

Explicado aquí: https://github.com/robbyrussell/oh-my-zsh/issues/449

En caso de que la URL quede muerta, la parte importante es:

Escape the ^ en su comando

Alternativamente, puede usar HEAD ~ para no tener que escapar cada vez.

Greg Hilston
fuente
15
Nunca recuerdo este comando y tengo que buscarlo en Google y encontrar mi propia respuesta jajajaja
Greg Hilston
2
git reset HEAD^está trabajando en zsh para mí, puede haber sido reparado.
Ben Kolya Mansley
@BenKolyaMansley ¿Qué versión de zsh estás ejecutando?
Greg Hilston
Estoy usandozsh 5.3 (x86_64-apple-darwin18.0)
Ben Kolya Mansley
7

En mi caso, ya presioné al repositorio. ¡Ay!

Puede revertir una confirmación específica mientras mantiene los cambios en sus archivos locales haciendo:

git revert -n <sha>

De esta manera pude mantener los cambios que necesitaba y deshacer una confirmación que ya se había presionado.

Wim Feijen
fuente
En mi caso, necesitaba revertir algo que ya había enviado al repositorio remoto. ¡Aún más ay! La mejor manera era git revert bad-commit-sha, entonces git revert -n revert-commit-just-created-shay luego reparar desde allí. Me tienes a medio camino. ¡Gracias!
TinkerTenorSoftwareGuy
Esto parece hacer lo contrario, ¿no? Crea nuevos cambios que corresponden a una reversión de los cambios realizados en la confirmación seleccionada. Si tuviera que confirmar estos nuevos cambios, habría deshecho el trabajo que realmente quería conservar.
svaens
3

El uso de git 2.9 (precisamente 2.9.2.windows.1) git reset HEAD^solicita más; No estoy seguro de lo que se espera entrada aquí. Por favor, consulte la siguiente captura de pantalla

ingrese la descripción de la imagen aquí

Encontramos otra solución git reset HEAD~#numberOfCommitscon la que podemos elegir seleccionar el número de confirmaciones locales que desea restablecer manteniendo sus cambios intactos. Por lo tanto, tenemos la oportunidad de descartar todos los compromisos locales, así como un número limitado de compromisos locales.

Consulte las siguientes capturas de pantalla que se muestran git reset HEAD~1en acción: ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

nkharche
fuente
Probablemente necesite escapar del carácter ^ - intente git reset "HEAD ^" o git reset HEAD \ ^
Mark Fisher
Además de esto, un git reset HEAD ^^ funciona como un solo ^ se trata como una nueva línea.
John Oss
2

Una forma más de hacerlo.

Agregue commit en la parte superior de commit temporal y luego haga:

git rebase -i

Para fusionar dos confirmaciones en una (el comando abrirá un archivo de texto con instrucciones explícitas, edítelo).

Ruslan Osipov
fuente
2
Técnicamente correcto pero nada tan elegante como simplemente hacerlo git reset HEAD^. Git rebase tiene mucho margen de error aquí.
TheBuzzSaw
0

2020 forma simple:

git reset <commit_hash>

(El hash de confirmación de la última confirmación que desea mantener).

Si se envió el commit, puede hacer lo siguiente:

git push -f

Mantendrá los cambios ahora no confirmados localmente

Xys
fuente