git reset

Git reset y los tres árboles
El comando git reset es una herramienta utilizada para deshacer cambios. Tiene tres formas de invocación que corresponden a los tres sistemas internos de gestión de estado de Git, llamados los tres árboles de Git. Estos sistemas incluyen HEAD (el historial de commits), el índice de staging y el directorio de trabajo. Vamos a revisar cada uno de estos sistemas.
El directorio de trabajo
El primer árbol es el directorio de trabajo. Representa los archivos del sistema de archivos de tu computadora, que están disponibles para que el editor de código aplique cambios. El directorio de trabajo se considera un commit específico del proyecto extraído. Cuando el proyecto se extrae, esto significa que sus archivos tienen versiones descomprimidas extraídas del repositorio Git.
git reset command
echo 'hello git reset' > edited_file
git status
#On branch master
#Changes not staged for commit:
#(use "git add ..." to update what will be committed)
#(use "git checkout -- ..." to discard changes in working directory)
#modified: edited_fileÍndice de staging
El siguiente árbol es el índice de staging, que rastrea los cambios preparados en el directorio de trabajo. En general, Git oculta a los usuarios los detalles de rendimiento del área de staging. A veces, al hablar del área de staging, se usan distintas expresiones, como caché, caché de directorio, archivos preparados, área de staging, etc.
Aquí necesitamos git ls-files. command, que se considera una herramienta de depuración, y que comprueba el estado del índice de staging.
git ls-files -s
git ls-files -s
#543644 a32de29bb3c1d643328b29ae775ad8c2e48c3256 0 edited_fileHistorial de commits
El último árbol es el historial de commits. El comando git commit confirma los cambios en una instantánea permanente colocada en el índice de staging.
git reset, commit history
git commit -am "edit content of test_file"
#[master ab23324] edit the content of edited_file
#1 file changed, 1 insertion(+)
git status
#On branch master
#nothing to commit, working tree cleanEn el ejemplo anterior, puedes ver el nuevo commit con el mensaje "edit content of test_file". Los cambios están adjuntos al historial de commits. En esta etapa, al ejecutar git status no se muestran cambios pendientes en ninguno de los árboles. Al invocar git log, verás el historial de commits. Una vez que los cambios se realizan a través de los tres árboles, se puede usar git reset.
Cómo funciona
A primera vista, el comando git reset tiene algunas similitudes con git checkout, ya que ambos operan sobre HEAD. El comando git checkout opera exclusivamente sobre el puntero de referencia HEAD, mientras que el comando git reset pasa el puntero de referencia HEAD y el puntero de referencia de la rama actual. Comprenderás mejor su comportamiento con la ilustración de abajo:

Esta ilustración presenta la secuencia de commits en la rama master. Como puedes ver, la referencia HEAD y la referencia de la rama master apuntan actualmente al commit d. Veremos cómo cambia la imagen en caso de git checkout b y git reset b.
git checkout b
Al ejecutar el comando git checkout, la referencia master sigue apuntando al commit d. En cuanto a la referencia HEAD, se ha movido y ha cambiado el puntero al commit b. Como resultado, el repositorio ahora está en un estado de 'detached HEAD'.

git reset b
El comando git reset cambia tanto HEAD como las referencias de la rama al commit definido. Además, cambia el estado de los tres árboles. Hay tres argumentos de línea de comandos --soft, --mixed y --hard que definen la modificación de los árboles del índice de staging y del directorio de trabajo.

Opciones principales
De forma predeterminada, el comando git reset tiene los argumentos constantes --mixed y HEAD. Por lo tanto, invocar git reset es lo mismo que invocar git reset --mixed HEAD. Aquí HEAD es el commit indicado. Puedes usar cualquier hash de commit SHA-1 de Git en su lugar.

--hard
La opción más utilizada es --hard. Sin embargo, usarla tiene algunos riesgos. Con --hard, los punteros de referencia del historial de commits empiezan a apuntar al commit indicado. Después, el índice de staging y el directorio de trabajo se restablecen para corresponder al commit indicado. Los cambios que estaban pendientes previamente en el índice de staging y en el directorio de trabajo se restablecen para coincidir con el estado del árbol del commit. Cualquier cambio pendiente en el índice de staging y en el directorio de trabajo se perderá. El siguiente ejemplo demostrará lo mencionado arriba. Antes que nada, ejecuta los siguientes comandos:
git reset usage
echo 'test content' > test_file
git add test_file
echo 'modified content' >> edited_fileSe ha creado un nuevo archivo llamado test_file y se ha añadido al repositorio. Además, el contenido de edited_file se modificará. Ahora comprobemos el estado del repositorio con estos cambios usando el comando git status.
git status
git status
#On branch master
#Changes to be committed:
#(use "git reset HEAD ..." to unstage)
#new file: test_file
#Changes not staged for commit:
#(use "git add ..." to update what will be committed)
#(use "git checkout -- ..." to discard changes in working directory)
#modified: edited_fileComo puedes ver, ahora hay algunos cambios pendientes. El cambio pendiente para el árbol del índice de staging es la adición de test_file, y el del directorio de trabajo son las modificaciones de edited_file. Ahora veamos el estado del índice de staging:
git ls-files -s
git ls-files -s
#123126 7a32454a5477b1bf4765946147c49509a431f963 0 test_file
#123126 6c423c1b04b5edd5acfc85de0b592449e5303773 0 edited_fileEl test_file se ha añadido al índice. El edited_file se ha actualizado, pero el SHA del índice de staging (d7d77c1b04b5edd5acfc85de0b592449e5303770) sigue siendo el mismo. Estos cambios están en el directorio de trabajo. No se han promovido al índice de staging, ya que no hemos usado el comando git add. En este punto, podemos ejecutar git reset --hard y ver el nuevo estado del repositorio:
git reset --hard
git reset --hard
#HEAD is now at ab23324 update content of edited_file
git status
#On branch master
#nothing to commit, working tree clean
git ls-files -s
#123126 6c423c1b04b5edd5acfc85de0b592449e5303773 0 edited_fileLa opción --hard ejecutó un "hard reset". Git indica que HEAD apunta al commit reciente ab23324. Luego, el estado del repositorio se comprueba con git status. Git indica que no hay cambios pendientes. En cuanto al estado del índice de staging, se ha restablecido a un punto anterior a la adición de test_file. Los cambios de edited_file y la adición de test_file se han eliminado. Esta pérdida no se puede deshacer.
--mixed
El modo de operación es, por defecto, --mixed. Actualiza los punteros de referencia. El índice de staging se restablece al commit indicado. Los cambios deshechos del índice de staging se colocan en el directorio de trabajo.
the git reset command
echo 'new file content' > test_file
git add test_file
echo 'append content' >> edited_file
git add edited_file
git status
#On branch master
#Changes to be committed:
#(use "git reset HEAD ..." to unstage)
#new file: test_file
#modified: edited_file
git ls-files -s
#123126 6a32154a5477b1bf4765946147c49509a4323d32 0 test_file
#123126 3c3262db063f9e9426901092c00a3394b4bd3445 0 edited_fileEn el ejemplo anterior, se ha añadido un test_file y se ha modificado el contenido de edited_file. A continuación, estos cambios se aplican al índice de staging con la ayuda de git add. Con este estado del repositorio, ahora es el momento de invocar git reset.
git reset --mixed
git reset --mixed
git status
#On branch master
#Changes not staged for commit:
#(use "git add ..." to update what will be committed)
#(use "git checkout -- ..." to discard changes in working directory)
#modified: edited_file
#Untracked files:
#(use "git add ..." to include in what will be committed)
#test_file
#no changes added to commit (use "git add" and/or "git commit -a")
git ls-files -s
#123126 6c423c1b04b5edd5acfc85de0b592449e5303773 0 edited_file--mixed es el modo predeterminado. Tiene el mismo efecto que git reset. git status muestra que hay cambios en edited_file y que test_file es un archivo sin seguimiento. Este es exactamente el comportamiento de --mixed. El índice de staging se ha restablecido y los cambios pendientes se trasladan al directorio de trabajo.
--soft
El argumento --soft actualiza los punteros de referencia y detiene el restablecimiento. Sin embargo, no afecta al índice de staging ni al directorio de trabajo.
git reset --soft
git reset --soft
git status
#On branch master
#Changes to be committed:
#(use "git reset HEAD ..." to unstage)
#modified: edited_file
git ls-files -s
#123126 32a252710639e5da6b515416fd779d0741e4561a 0 edited_fileUn soft reset restablece solo el historial de commits. De forma predeterminada, se invoca con HEAD como commit de destino. Ahora vamos a crear un nuevo commit para probar un --soft con un commit de destino que no sea HEAD:
git reset and commit
git commit -m "add changes to edited_file"Ahora nuestro repositorio tiene tres commits. Para encontrar el primero, necesitamos comprobar su ID, lo cual puede hacerse viendo la salida de git log.
git reset and git log
git log
#commit 62e793f6941c7e0d4ad9a1345a175fe8f45cb9df
#Author: w3docs
#Date: Fri Nov 1 14:02:07 2019 -0800
#add changes to edited_file
#commit ab23324a6da9f0dec51ed16d3d8823f28e1a72a
#Author: w3docs
#Date: Fri Nov 1 11:31:58 2019 -0800
#change content of edited_file
#commit 780411da3b47117270c0e3a8d5dcfd11d28d04a4
#Author: w3docs
#Date: Thu Sep 31 18:40:29 2019 -0800
#initial commitEste es el ID del commit inicial. Ahora se usará como destino para el soft reset. Antes, necesitamos comprobar el estado actual del repositorio:
git status && git ls-files -s
git status && git ls-files -s
#On branch master
#nothing to commit, working tree clean
#123126 32a252710639e5da6b515416fd779d0741e4561a 0 edited_fileAhora podemos hacer un soft reset al primer commit:
git reset --soft
git reset --soft 780411da3b47117270c0e3a8d5dcfd11d28d04a4
git status && git ls-files -s
#On branch master
#Changes to be committed:
#(use "git reset HEAD ..." to unstage)
#modified: edited_file
#123126 32a252710639e5da6b515416fd779d0741e4561a 0 edited_fileEn el ejemplo anterior, hicimos un soft reset y se invocó el comando combinado git status y git ls-files, que muestra el estado del repositorio. El comando git status muestra que hay algunos cambios en edited_file, destacándolos como cambios preparados para el siguiente commit. La entrada de git ls-files muestra que el índice de staging no ha cambiado y conserva el SHA 32a252710639e5da6b515416fd779d0741e4561a. Examinemos además el estado del repositorio después del soft reset con la ayuda de git log:
git reset
git log
#commit 780411da3b47117270c0e3a8d5dcfd11d28d04a4
#Author: w3docs
#Date: Thu Sep 31 18:40:29 2019 -0800
#initial commitComo podemos ver, la salida anterior indica que hay un solo commit en el historial de commits. Como en todas las invocaciones de git reset, primero --soft restablece el árbol de commits.
A diferencia de --hard y --mixed, que actúan sobre HEAD, un soft reset retrocedió el árbol de commits en el tiempo.
La diferencia entre los comandos reset y revert
Git revert se considera una forma más segura de deshacer cambios que git reset. Existe una gran probabilidad de que el trabajo pueda perderse con git reset. git reset no elimina un commit, pero puede dejar el commit “huérfano”. Esto significa que no hay ninguna forma directa de acceder a él. Como resultado, Git eliminará todos los commits huérfanos cuando ejecute el recolector interno de basura. De forma predeterminada, Git ejecuta el recolector interno de basura cada 30 días. Los commits huérfanos suelen encontrarse con la ayuda del comando git reflog.
Otra diferencia entre estos dos comandos es que git revert está configurado para deshacer commits públicos, y git reset está configurado para deshacer cambios locales en el directorio de trabajo y el índice de staging.
La inadmisibilidad de restablecer el historial público
No uses git reset <commit> cuando haya instantáneas posteriores a <commit> que se hayan movido a un repositorio público. Cuando publiques un commit, ten en cuenta que otros desarrolladores también dependen de él. Eliminar commits en los que también están trabajando otros miembros del equipo causará muchos problemas. Usa git reset <commit> solo en cambios locales. Para corregir cambios públicos, usa el comando git revert.
Ejemplos
Usa lo siguiente para eliminar el archivo especificado del área de staging, pero sin cambiar el directorio de trabajo. Despreparará un archivo sin sobrescribir cambios:
git reset file
git reset fileUsa lo siguiente para restablecer el área de staging para que corresponda al último commit, pero dejando el directorio de trabajo sin cambios. Despreparará todos los archivos sin sobrescribir cambios, dándote la posibilidad de reconstruir la instantánea preparada desde cero:
git reset staging area
git resetUsa lo siguiente para restablecer el área de staging y el directorio de trabajo para que correspondan al último commit. También despreparará cambios sobrescribiendo todos los cambios en el directorio de trabajo:
git reset --hard command
git reset --hardUsa lo siguiente para mover la punta de la rama hacia atrás en el tiempo hasta el commit, restableciendo el área de staging para que coincida, pero sin tocar el directorio de trabajo:
git reset commit
git reset commitUsa lo siguiente para mover la punta de la rama actual hacia atrás hasta el commit y restablecer el área de staging y el directorio de trabajo para que coincidan:
git reset --hard commit
git reset --hard commitEliminación de commits locales
Como se mencionó arriba, puedes usar el comando git reset para eliminar commits en el repositorio local. El siguiente ejemplo muestra un uso de git reset. El comando git reset --hard HEAD~2 retrocede la rama actual dos commits y elimina las dos instantáneas creadas recientemente del historial del proyecto.
git add command
# Create a new file called `yourname.txt` and add some code to it
# Commit it to the project history
git add yourname.txt
git commit -m "Start to develop a project"
# Edit `yourname.txt` again and change some other tracked files, too
# Commit another snapshot
git commit -a -m "Continue developing"
# Scrap the project and remove the related commits
git reset --hard HEAD~2Despreparar archivos
El comando git reset suele usarse para crear instantáneas preparadas. En el siguiente ejemplo tenemos 2 archivos llamados task.txt e index.txt, que se han añadido al repositorio. git reset nos permite quitar del área de staging los cambios que no están relacionados con el siguiente commit.
git reset unstaging files
# Edit task.txt and index.txt
# Stage everything in the current directory
git add .
# Realize that the changes in task.txt and index.txt
# should be committed in different snapshots
# Unstage index.txt
git reset index.txt
# Commit only task.txt
git commit -m "Edit task.txt"
# Commit index.txt in a separate snapshot
git add index.txt
git commit -m "Edit index.txt"Practice
What are the features and options of the 'git reset' command in Git?