Bienvenido a nuestra guía definitiva sobre los comandos  git reset y git revert . Este tutorial te enseñará todo lo que necesitas saber sobre como corregir errores comunes y deshacer commits erróneos mientras usas Git.

Entendiendo las tres secciones de un proyecto Git

Un proyecto Git tiene las siguientes tres secciones principales:

  1. Directorio Git
  2. Directorio de trabajo (o árbol de trabajo)
  3. Zona de "staging"

El directorio Git (ubicado en RUTA-DE-TU-PROYECTO/.git/) es donde Git almacena todo lo que necesita para hacer un seguimiento preciso del proyecto. Esto incluye metadatos y una base de datos de objetos que incluye versiones comprimidas de los archivos del proyecto.

El directorio de trabajo es donde un usuario realiza cambios locales en un proyecto. El directorio de trabajo descarga los archivos del proyecto de la base de datos de objetos del directorio Git y los coloca en la máquina local del usuario.

Nota: Directorio también se conoce como repositorio o en forma abreviada repo. El repo en la máquina local del usuario se llama "Repo local" mientras que el repo en el servidor git se llama "Repo remoto".

La zona de "staging" es un archivo (también llamado "index", "stage" o "cache") que almacena información sobre lo que irá en tu próximo commit. Un commit es cuando le dices a Git que guarde estos cambios staged. Git toma una instantánea de los archivos tal y como están y almacena permanentemente esa instantánea en el directorio Git.

Con tres secciones, hay tres estados principales, en los que puede estar un archivo en un momento dado: modificado, commited, o staged. Tú modificas un archivo cada vez que se hacen cambios en él directorio de trabajo. Luego es staged cuando lo mueves al zona de "staging". Finalmente, es committed después de un commit.

Git Reset

El comando git reset te permite RESTABLECER tu estado actual a un estado específico. Puedes restablecer el estado de archivos específicos, así como el de todo una rama. Esto es útil si aún no has subido tu commit a GitHub o a otro repositorio remoto.

Reset un archivo o un conjunto de archivos

El siguiente comando te permite elegir selectivamente los trozos de contenido y revertirlos o unstage los archivos.

git reset (--patch | -p) [tree-ish] [--] [ruta]

Unstage un archivo

Si has movido un archivo la zona de "staging" con git add, pero ya no quieres que forme parte del commit, puedes usar git reset para unstage ese archivo:

git reset HEAD ARCHIVO-A-UNSTAGE

Los cambios que has hecho seguirán estando en el archivo, este comando solo remueve el archivo de tu zona de "staging".

Restablecer una rama a un commit anterior

El siguiente comando restablece el HEAD de tu rama actual al COMMIT dado y actualiza el índice. Básicamente rebobina el estado de su rama, luego todos los commits que hagas en adelante se escriben sobre todo lo que vino después del punto de reinicio. Si omites el MODE, el valor predeterminado es --mixed:

git reset MODE COMMIT

Las opciones para MODE son:

  • --soft: no restablece el fichero índice o el árbol de trabajo, pero restablece HEAD para commit. Cambia todos los archivos a "Cambios a ser commited".  
  • --mixed: restablece el índice, pero no el árbol de trabajo e informa de lo que no se ha actualizado.
  • --hard: restablece el índice y el árbol de trabajo. Cualquier cambio en los archivos rastreados en el árbol de trabajo desde el commit son descartados.
  • --merge: restablece el índice y actualiza los archivos en el árbol de trabajo que son diferentes entre el commit y HEAD, pero mantiene los que son diferentes entre el índice y el árbol de trabajo.
  • --keep: restablece las entradas del índice, actualiza los archivos en el árbol de trabajo que son diferentes entre commit y HEAD. Sin un archivo que es diferente entre commit y HEAD tiene cambios locales, el reinicio se aborta.

Nota importante sobre los Hard Resets

Ten mucho cuidado usando la opción --hard con git reset, ya que restablece tu commit, zona de "staging" y tu directorio de trabajo. Si esta opción no se utiliza correctamente, se puede acabar perdiendo el código escrito.

Git Revert

Ambos comandos git revert y git reset, deshacen commits anteriores. Pero si ya has subido tu commit a un repositorio remoto, se recomienda que no uses git reset, ya que reescribe el historial de commits. Esto puede hacer que trabajar en un repositorio con otros desarrolladores y mantener un historial consistente de commits sea muy difícil.

En su lugar es mejor usar git revert, que deshace los cambios realizados por un commit anterior creando un commit completamente nuevo, todo esto sin alterar el historial de commits.

Revert un commit o un grupo de commits

El siguiente comando te permite revertir los cambios de un commit o varios commits anteriores y crear un nuevo commit.

git revert [--[no-]edit] [-n] [-m parent-number] [-s] [-S[<keyid>]] <commit>…
git revert --continue
git revert --quit
git revert --abort

Opciones comunes:

  -e
  --edit
  • Esta es la opción por defecto y no necesitar ser establecida explícitamente. Abre el editor de texto por defecto de tu sistema y te permite editar el nuevo mensaje commit antes de commit el revert.
  • Esta opción hace lo contrario de -e, y git revert no abrirá el editor de texto.
  • Esta opción evita que git revert deshaga un commit anterior y cree uno nuevo. En lugar de crear un nuevo commit,-n deshará los cambios del commit anterior y los añadirá al staging index y directorio de trabajo.
  --no-edit
-n
-no-commit

Ejemplo.

Imaginemos la siguiente situación: 1.) Estás trabajando en un archivo y añades y haces commit a tus cambios. 2.) A continuación trabajas en otras cosas y haces algunos commits más. 3.) Ahora te das cuenta de que, hace tres o cuatro commits, hiciste algo que te gustaría deshacer - ¿cómo puedes hacerlo?

Podrías pensar, simplemente usa git reset, pero esto eliminará todos los commits después del que quieres cambiar - ¡git revert al rescate! Veamos este ejemplo:

mkdir learn_revert # Crea una carpeta llamada `learn_revert`
cd learn_revert # `cd` adentro de la carpeta `learn_revert`
git init # Inicializa un repositorio de git

touch first.txt # Crea un archivo llamado `first.txt`
echo Start >> first.txt # Agrega el texto "Start" a `first.txt`

git add . # Agrega el archivo `first.txt`
git commit -m "adding first" # Commit con el mensaje "Adding first.txt"

echo WRONG > wrong.txt # Agrega el texto "WRONG" a `wrong.txt`
git add . # Agrega el archivo `wrong.txt`
git commit -m "adding WRONG to wrong.txt" # Commit con el mensaje "Adding WRONG to wrong.txt"

echo More >> first.txt # Agrega el texto "More" a `first.txt`
git add . # Agrega el archivo `first.txt`
git commit -m "adding More to first.txt" # Commit con el mensaje "Adding More to first.txt"

echo Even More >> first.txt # Agrega el texto "Even More" a `first.txt`
git add . # Agrega el archivo `first.txt`
git commit -m "adding Even More to First.txt" # Commit con el mensaje "Adding More to first.txt"

# ¡OH NO! Queremos deshacer el commit con el texto "WRONG" - ¡vamos a revertir! Como este commit fue el segundo de donde no estamos, podemos usar git revert HEAD~2 (o podemos usar git log y encontrar el SHA de ese commit)

git revert HEAD~2 # esto nos pondrá en un editor de texto en donde podemos modificar el mensaje del commit.

ls # wrong.txt ¡ya no está allí!
git log --oneline # nota que el historial de commit no ha sido alterado, solo hemos agregado un nuevo commit reflejando la eliminación del `wrong.txt`

Y con eso estás un paso más cerca de conseguir tu cinturón negro en Git.

Traducido del árticulo - The Ultimate Guide to Git Reset and Git Revert