Cómo deshacer el último commit en git

Aquí os dejo una manera sencilla de dar marcha atrás si habéis hecho un commit y os habéis arrepentido de hacerlo. El motivo por el que queréis «borrarlo» puede ser múltiple: porque el trabajo no está terminado y queréis continuar trabajando, habéis introducido un bug sin daos cuenta o sencillamente os habéis equivocado y lo habéis hecho antes de tiempo.

Existen dos maneras de borrar ese commit:

  • Eliminando junto al commit las modificaciones que este contiene
  • Recuperándolas en el área de trabajo para seguir trabajando en ellas

En ambos casos, el comando que utilizaremos será «git reset».

Deshacer el commit perdiendo las modificaciones

Supongamos que tenemos nuestro repositorio en el siguiente estado:

Voila_Capture283y queremos deshacer el último commit. En este primer caso, queremos desechar los cambios introducidos en ese commit que contiene una serie de tests funcionales.

Para ello, ejecutamos el comando:

git reset --hard HEAD~1

Tras ejecutar el comando, el estado del repositorio es el siguiente:

Repositorio tras borrar el commit

Como podéis ver, el commit 600cc08 ha desaparecido que era lo que queríamos. Además, la rama activa se ha desplazado un commit hacia abajo y nuestro área de trabajo ha quedado en el estado del commit 6eb9f2d. Los tests funcionales que estaban en el commit se han perdido y tendríamos que recurrir al reflog para recuperarlos.

La sintaxis HEAD~1 del comando anterior la podríamos traducir como «El commit al que está apuntando la rama activa menos uno». Si hubiésemos ejecutado el comando:

git reset --hard HEAD~3

en lugar de acabar en el commit 6eb9f2d (uno por detrás) habríamos acabado en 63db9fa (tres por detrás).

Deshacer el commit manteniendo las modificaciones

Existe la posibilidad de eliminar el commit pero manteniendo las modificaciones que contiene ese commit en el área de trabajo. ¿Y por qué querríamos hacer esto? Por varios motivos, por ejemplo por que los tests funcionales del commit 600cc08 están incompletos, son incorrectos o he introducido algún bug en él.

Partiendo de nuevo del mismos estado inicial de antes:

Estado inicial del repositorio.ejecutaríamos el siguiente comando:

git reset HEAD~1

Tras lo cual, el estado del repositorio sería:

Voila_Capture285Si miráis el estado veréis los siguiente:

  • Al igual que en el caso anterior, el commit 600cc08 ha desaparecido
  • La rama activa (rest) ha pasado al apuntar al commit 6eb9f2d
  • A diferencia del  caso anterior, el área de trabajo contiene las modificaciones que estaban en el commit que acabamos de borrar.

Así que podemos seguir trabajando, corregir el bug o completar los tests que habíamos dejado incompletos y hacer un nuevo commit con los cambios completos. ¡Así de fácil!

Muy útil pero…

Muy importante tener en cuenta que estas dos operaciones sobreescriben la historia del repositorio ¡estamos borrando un commit!. Si estamos trabajando en local y no hemos hecho push a nuestro remoto no hay ningún problema. Si ha habéis hecho push de este commit tened en cuenta que vuestros compañeros lo seguirán viendo si alguna de sus ramas lo referencia.

Espero que os haya resultado útil.

 

 

15 comentarios en “Cómo deshacer el último commit en git

    1. alfonso Autor

      Hola Rafa:

      Efectivamente, como bien pones en tu post está la opción git reset –soft HEAD~1. La opción por defecto es –mixed. La diferencia entre las dos:

      –mixed: mantiene los ficheros pero no los marca como para hacer commit
      –soft: mantiene los ficheros y los marca para hacer commit. Si ejecutas un git status, te aparecerán en la sección «Changes to be commited»

      ¡Gracias por la aclaración!

      P.D. En la página de manual están las tres opciones: –hard –soft –mixed (https://www.kernel.org/pub/software/scm/git/docs/git-reset.html)

  1. Mario

    Hola!

    Vengo de Subversion y se me esta haciendo muy complicado comprender git. Esta explicación me lo ha dejado claro. Gracias!

    Un saludo!:)

  2. hector

    Hola cuando realizo un git rebase -i HEAD~2 y utlilizo el edit en el ultimo commit
    y me di cuenta que quiero eliminar el ultimo commit realizo git reset HEAD^
    apreto enter y me sale por consola ¿Más? por lo que no se que estoy haciendo mal.
    Si me pueden orientar se los agradeceria.
    Saludos

    1. admin

      Hola Hector:

      cuando has ejecutado el comando git reset ¿la consola sólo te ha mostrado el mensaje «¿Más?» ? No recuerdo haber visto ese mensaje.

      En cualquier caso, si has ejecutado «git reset HEAD^» estás utilizando la opción –mixed del comando reset de manera implícita y esto hace que tras ejecutar el comando que has puesto, el último commit se «elimine» y que las modificaciones del mismo permanecen en tu área de trabajo. De esta manera, aunque el commit lo dejes de ver, el código fuente sigue disponible en tu área de trabajo. Al ejecutar este comando de git, no te sale ningún mensaje por consola, git lo ejecuta y ya está.

      Si quieres «eliminar» completamente ese commit y que el código fuente que se ha modificado vuelva estar como estaba antes de hacerlo, tendrías que ejecutar el comando «git reset –hard HEAD^». Pero ten mucho cuidado porque la opción –hard del comando reset borra cualquier cambio que tengas en tu área de trabajo. Antes de hacerlo asegurate de que tu área de trabajo está limpia y si no lo está usa el comando git stash para guardarla. Los comandos serían:

      • git stash
      • git reset –hard HEAD^
      • git stash pop

      ¿Te he aclarado un poco lo que me estabas preguntando?

  3. Pingback: Mis Comandos Gits – Pedro Caicedo

  4. JAvi

    Buenos días,
    en mi caso no es que me interese borrar un commit tal cual, sino que tengo la problemática de que necesito borrar ciertos ficheros que he subido en un commit.

    Son ficheros que nunca mas voy a utilizar, ni siquiera para hacer un rollback ¿Como podría hacer ese borrado?

    un saludo
    Javi

    1. admin

      Hola Javi:

      Si lo que quieres es borrar ciertos ficheros debes usar el comando «git rm». Por ejemplo:


      #git rm fichero1.txt app/fichero2.txt
      #git commit -m'Borrando ficheros!!'

      A partir de este commit, los ficheros ya no estarán. Ten en cuenta, es sí, que si haces checkout de un commit anterior los ficheros volverán a aparecer en tu área de trabajo.

      1. JAvi

        Muy buenas,
        no es que quiera borrar un fichero.

        Pongamos siguiente ejemplo de ficheros que subo por commit:

        Commit 1 –> fichero a modificado
        Commit 2 –> fichero a modificado
        Commit 3 –> fichero a modificado
        Commit 4 –> fichero a modificado

        De alguna manera sé, que el fichero «a» del commit 1 ya no lo voy a necesitar nunca mas.
        La pregunta viene en como podría borrar ese fichero.

        1. admin

          Creo que ahora entiendo mejor la pregunta. Según me indicas, imagina tienes un fichero «index.html» que modificas en 3 commits consecutivos (según el ejemplo que me has puesto).

          Supongamos que este es el contenido del fichero index.html en cada commit según me has indicado en el comentario anterior (en cada commit que haces modificas el fichero)


          # index.html Commit1
          <a href="pagina1.html" rel="nofollow">Página 1</a>


          # index.html Commit2
          <a href="pagina1.html" rel="nofollow">Página 1</a>
          <a href="pagina2.html" rel="nofollow">Página 2</a>


          # index.html Commit3
          <a href="pagina1.html" rel="nofollow">Página 1</a>
          <a href="pagina2.html" rel="nofollow">Página 2</a>
          <a href="pagina3.html" rel="nofollow">Página 3</a>

          Si quieres borrar el fichero «index.html» del primer commit, podrías hacerlo con un rebase interactivo en el que seleccionas la opción «edit» del primer commit para borrarlo. Eso sí, cuando regenere el segundo commit durante el rebase te va a dar conflictos ya que estarás modificando un fichero que en el paso anterior del rebase has borrado. Te pongo un ejemplo como hacerlo con un repo con commits reales:

          Imagina que este es tu repositorio:

          * d36db1d (master) Commit 3
          * d7c982c Commit 2
          * ac1a1c8 Commit 1
          * 49bbab1 Commit inicial

          Quieres borrar el fichero index del Commit 1 (ac1a1c8). El comando sería


          #git rebase -i 49bbab1

          Y en la interfaz que te sale para seleccionar las acciones del rebase interactivo estas quedarían:

          edit ac1a1c8 Commit 1
          pick d7c982c Commit 2
          pick d36db1d Commit 3

          Como te he comentado antes, en el primer paso del rebase al poner edit git te permite editar el commit en lugar de hacerlo automáticamente y ahí podrías borrar el fichero index.html. También podrías usar la opción «exec» del rebase interactivo y borrar el fichero ejecutando un comando rm.

          Cuando termines el primer paso del rebase interactivo y git genere el siguiente commit, te dará un conflicto ya que estarías intentando modificar el fichero index.html que acabas de borrar. Lo resuelves y continúas el rebase hasta que aplique todos los commits.

          ¿Te he respondido a la pregunta?

  5. Oscar

    Hola, buenos días.

    Hice un commit que quedó en el repositorio local, luego hice un revert pero se me perdió el último código que había generado. Es posible recuperar ese código? Qué puedo hacer?

    Gracias

  6. Daniel

    Ejemplo tengo una serie de commits

    3b4r4903
    549nf34
    4m3400
    904g4jf4

    Si yo quiero eliminar únicamente el 549nf34 pero quiero conservar los demás sin tener que eliminar el primero 3b4r4903 ,4m3400, 904g4jf4 ?

    Que comando tengo que usar?

    Que es lo que tengo que hacer?

    1. admin

      Hola Daniel:

      Acabo de ver este comentario metido entre el SPAM del blog. Disculpa que haya tardado tanto tiempo en responderte. Es una buena pregunta y aunque haya pasado tiempo, te respondo por si aún te sirve la respuesta.

      Para eliminar ese commit necesitas hacer un rebase interactivo.

      3b4r4903 (ramaX)
      549nf34
      4m3400
      904g4jf4
      aaabbbccc

      (date cuenta que a los commits que has puesto tu yo he añadido el commit aaabbbccc, que sería el padre de 904g4jf4)

      El comando sería:


      > git checkout ramaX
      > git rebase -i aaabbbccc

      Este comando abrirá un editor de texto con una pantalla similar a la siguiente:


      pick 904g4jf Comentario de este commit...
      pick 4m3400 Comentario de este commit...
      pick 549nf34 Comentario de este commit...
      pick 3b4r490 Comentario de este commit...

      # Rebase aaabbbccc..3b4r490 onto 3b4r490 (4 commands)
      #
      # Commands:
      # p, pick = use commit
      # r, reword
      = use commit, but edit the commit message
      # e, edit
      = use commit, but stop for amending
      # s, squash
      = use commit, but meld into previous commit
      # f, fixup
      = like "squash", but discard this commit's log message
      # x, exec = run command (the rest of the line) using shell
      # b, break = stop here (continue rebase later with 'git rebase --continue')
      # d, drop
      = remove commit
      # l, label

      Siguiendo las instrucciones que te muestra el comando en pantalla, en la línea correspondiente al commit 549nf34 sustituyas «pick» por «drop». Guarda y cierra tu editor y el rebase interactivo comenzará a ejecutarse.

      Si te salen conflictos (cosa que dependerá de lo que tengas en los commits) resuelvelos, sube los ficheros al staging area con un git add y luego continúa el rebase con git rebase –continue.

      Espero haberte resuelto la pregunta y, de nuevo, perdona el retraso.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *