Cómo deshacer "git commit --amend" hecho en lugar de "git commit"

1296

Accidentalmente modifiqué mi commit anterior. El commit debería haber sido separado para mantener el historial de los cambios que hice en un archivo en particular.

¿Hay alguna manera de deshacer ese último compromiso? Si hago algo así git reset --hard HEAD^, la primera confirmación también se deshace.

(Todavía no he empujado a ningún directorio remoto)

Jesper Rønn-Jensen
fuente

Respuestas:

2291

Lo que debe hacer es crear un nuevo commit con los mismos detalles que el HEADcommit actual , pero con el padre como la versión anterior de HEAD. git reset --softmoverá el puntero de rama para que la próxima confirmación se realice encima de una confirmación diferente de donde está ahora el encabezado de rama actual.

# Move the current head so that it's pointing at the old commit
# Leave the index intact for redoing the commit.
# HEAD@{1} gives you "the commit that HEAD pointed at before 
# it was moved to where it currently points at". Note that this is
# different from HEAD~1, which gives you "the commit that is the
# parent node of the commit that HEAD is currently pointing to."
git reset --soft HEAD@{1}

# commit the current tree using the commit details of the previous
# HEAD commit. (Note that HEAD@{1} is pointing somewhere different from the
# previous command. It's now pointing at the erroneously amended commit.)
git commit -C HEAD@{1}
CB Bailey
fuente
33
Muy guay, +1. Incluso lo hice con la segunda última vista de modificación git reflogpara encontrar el número correcto, por ejemplo {2}.
JJD
179
Para ser claros, el primer comando es un verdadero "deshacer". Produce el HEAD, el directorio de trabajo (sin cambios) y el estado del índice anterior a git commit --amend. El segundo es un "rehacer" en una nueva confirmación. Estos funcionan para cualquiera git commit, no solo --amend.
cdunn2001
6060
Entonces, si no lo modificó con un nuevo mensaje de confirmación que necesita recuperar, la segunda parte puede ser regular git commit.
Matt Montag
18
Por alguna razón, me estaba poniendo un error cuando se ejecuta git reset --soft HEAD@{1}: fatal: ambiguous argument 'HEAD@1': unknown revision or path not in the working tree. Use '--' to separate paths from revisions. Cuando lo reemplacé HEAD@{1}con el hash de confirmación equivalente que se muestra en git reflog(¡gracias JJD!), ¡Esta respuesta funcionó maravillosamente!
Tim Camber el
20
@TimArnold dependiendo de su shell, es posible que necesite poner comillas simples o dobles HEAD@{1}. Si corroecho HEAD@{1} en tcsh, por ejemplo, la salida se HEAD@1debe a que tcsh interpretó las llaves. Si uso comillas simples, se conservan las llaves.
Kelvin
136

usa el registro de referencia :

git branch fixing-things HEAD@{1}
git reset fixing-things

entonces debe tener todos sus cambios previamente modificados solo en su copia de trabajo y puede confirmar nuevamente

para ver una lista completa del tipo de índices anteriores git reflog

knittl
fuente
77
Esto también borra el índice, sigue siendo útil, pero va más allá de un simple "deshacer".
cdunn2001
3
¿Hay alguna diferencia entre HEAD@{1}y HEAD~1?
neaumusic
15
@neaumusic: sí! HEAD~1es exactamente igual HEAD^e identifica al padre de la confirmación actual. HEAD@{1}por otro lado, se refiere a la confirmación que HEAD señaló antes de esta, es decir, se refieren a confirmaciones diferentes cuando se paga una rama diferente o se modifica una confirmación.
knittl
@knittl ah, no es de extrañar que no pensara que esto fuera posible antes, gracias de nuevo, buena información
neaumusic
99
El primer paso es redundante. Lo simple git reset HEAD@{1}es suficiente.
Habita el
79

Encuentra tus confirmaciones modificadas por:

git log --reflog

Nota: Puede agregar --patchpara ver el cuerpo de los commits para mayor claridad. Igual que git reflog.

luego restablezca su HEAD a cualquier confirmación previa en el punto en que estaba bien:

git reset SHA1 --hard

Nota: Reemplace SHA1 con su hash de confirmación real. También tenga en cuenta que este comando perderá los cambios no confirmados, por lo que puede guardarlos antes. Alternativamente, use --soften su lugar para retener los últimos cambios y luego confirmarlos.

Luego selecciona el otro commit que necesitas además:

git cherry-pick SHA1
kenorb
fuente
26
Si lo hace git reset SHA1 --soft, puede retener los últimos cambios y luego confirmarlos.
pravj
24

Siempre puedes dividir un commit, desde el manual

  • Inicie un rebase interactivo con git rebase -i commit ^, donde commit es el commit que desea dividir. De hecho, cualquier rango de confirmación funcionará, siempre que contenga esa confirmación.
  • Marque la confirmación que desea dividir con la acción "editar".
  • Cuando se trata de editar esa confirmación, ejecute git reset HEAD ^. El efecto es que la CABEZA se rebobina por uno y el índice sigue su ejemplo. Sin embargo, el árbol de trabajo permanece igual.
  • Ahora agregue los cambios al índice que desea tener en la primera confirmación. Puede usar git add (posiblemente de forma interactiva) o git-gui (o ambos) para hacer eso.
  • Confirme el índice actual con cualquier mensaje de confirmación que sea apropiado ahora.
  • Repita los dos últimos pasos hasta que su árbol de trabajo esté limpio.
  • Continúe el rebase con git rebase - continúe.
Arkaitz Jiménez
fuente
26
demasiado complicado git refloges todo lo que necesitas
knittl
2
Muchos pasos sí, pero cada paso es sencillo y fácil de hacer. Esto funcionó para mí y obtiene mi voto.
OzBandit
55
Además, esta respuesta le permite elegir selectivamente los cambios que accidentalmente 'modificó', para proporcionar un valor adicional al enfoque git reset --soft HEAD @ {1} (que resolvió mi problema BTW)
Wiebe Tijsma
2
También puede elegir selectivamente los cambios con el método de reflog. Solo hazlo en git resetlugar de git reset --soft, luego hazlo git add --patch.
geekofalltrades
1
Esto todavía reescribe la historia y requiere un empuje forzado. Dependiendo de su situación, eso puede o no ser un problema.
Pajn
20

Posiblemente valga la pena señalar que si todavía está en su editor con el mensaje de confirmación, puede eliminar el mensaje de confirmación y abortará el git commit --amendcomando.

Justin Schulz
fuente
Éste es el indicado.
atilkan
Salvó mi pero ^^
engineeringcoding
14

Tal vez pueda usar git reflog para obtener dos commit antes de enmendar y después de enmendar.

Luego usa git diff before_commit_id after_commit_id > d.diff para obtener diferencias entre antes de enmendar y después de enmendar.

Siguiente uso git checkout before_commit_id para volver a antes de confirmar

Y ultimo uso git apply d.diff para aplicar el cambio real que hiciste.

Eso resuelve mi problema.

utzcoz
fuente
11

Si ha empujado el commit a remoto y luego ha modificado erróneamente los cambios a ese commit, esto solucionará su problema. Emita a git logpara encontrar el SHA antes de la confirmación. (esto supone que el control remoto se denomina origen). Ahora emita estos comandos usando ese SHA.

git reset --soft <SHA BEFORE THE AMMEND>
#you now see all the changes in the commit and the amend undone

#save ALL the changes to the stash
git stash

git pull origin <your-branch> --ff-only
#if you issue git log you can see that you have the commit you didn't want to amend

git stash pop
#git status reveals only the changes you incorrectly amended

#now you can create your new unamended commit
David Sopko
fuente
3
Este es un caso especial de la pregunta más general, pero cubrió exactamente mi necesidad inmediata.
dmckee --- ex-gatito moderador
8

Puede hacer a continuación para deshacer su git commit —amend

  1. git reset --soft HEAD^
  2. git checkout files_from_old_commit_on_branch
  3. git pull origin your_branch_name

====================================

Ahora sus cambios son como los anteriores. Así que ya terminaste de deshacergit commit —amend

Ahora puedes hacerlo git push origin <your_branch_name>, empujar a la rama.

Pratik
fuente
3

Casi 9 años tarde a esto, pero no vi esta variación mencionada logrando lo mismo (es una especie de combinación de algunos de estos, similar a la respuesta principal ( https://stackoverflow.com/a/1459264/4642530 ) .

Buscar todas las cabezas separadas en la rama

git reflog show origin/BRANCH_NAME --date=relative

Luego encuentra el hash SHA1

Restablecer a viejo SHA1

git reset --hard SHA1

Luego empújalo hacia arriba.

git push origin BRANCH_NAME

Hecho.

Esto lo revertirá a la antigua confirmación por completo.

(Incluyendo la fecha de la cabeza de compromiso separada sobrescrita anterior)

garrettmac
fuente
Sí, pero normalmente quiero restablecer --softpara mantener mis cambios. Solo quiero que se cometa por separado
Juan Mendes
2
  1. Pago a sucursal temporal con la última confirmación

    git branch temp HEAD@{1}

  2. Restablecer la última confirmación

    git reset temp

  3. Ahora, tendrá todos los archivos de su confirmación, así como la confirmación anterior. Verifique el estado de todos los archivos.

    git status

  4. Restablezca sus archivos de confirmación desde la etapa git.

    git reset myfile1.js (pronto)

  5. Vuelva a adjuntar este compromiso

    git commit -C HEAD@{1}

  6. Agregue y confirme sus archivos a una nueva confirmación.

Priyanshu Chauhan
fuente