Git Reset vs Revertir vs Rebase

En este artículo, aprenderá sobre diferentes formas de jugar con confirmaciones en Git.

Como desarrollador, se habría enfrentado a este tipo de situaciones varias veces en las que hubiera querido retroceder a una de sus confirmaciones anteriores, pero no está seguro de cómo hacerlo. E incluso si conoce los comandos de Git como reset, revert, rebase, no es consciente de las diferencias entre ellos. Entonces, comencemos y comprendamos qué son git reset, revert y rebase.

Restablecer Git

Git reset es un comando complejo y se usa para deshacer los cambios.

Puede pensar en git reset como una función de reversión. Con git reset, puedes saltar entre varias confirmaciones. Hay tres modos de ejecutar un comando git reset: –soft, –mixed y –hard. De forma predeterminada, el comando git reset usa el modo mixto. En un flujo de trabajo de restablecimiento de git, aparecen tres mecanismos de administración interna de git: HEAD, área de preparación (índice) y el directorio de trabajo.

El directorio de trabajo es el lugar donde está trabajando actualmente, es el lugar donde están presentes sus archivos. Usando un comando de estado de git, puede ver qué archivos/carpetas están presentes en el directorio de trabajo.

Staging Area (Índice) es donde git rastrea y guarda todos los cambios en los archivos. Los cambios guardados se reflejan en el directorio .git. Utiliza git add «filename» para agregar el archivo al área de preparación. Y como antes, cuando ejecuta git status, verá qué archivos están presentes en el área de ensayo.

La rama actual en Git se conoce como HEAD. Apunta a la última confirmación, que ocurrió en la rama de pago actual. Se trata como un puntero para cualquier referencia. Una vez que realiza el pago en otra sucursal, el HEAD también se traslada a la nueva sucursal.

Permítanme explicar cómo funciona git reset en los modos duro, suave y mixto. El modo difícil se usa para ir a la confirmación señalada, el directorio de trabajo se llena con archivos de esa confirmación y el área de preparación se reinicia. En el restablecimiento parcial, solo el puntero se cambia a la confirmación especificada. Los archivos de todas las confirmaciones permanecen en el directorio de trabajo y el área de preparación antes del reinicio. En el modo mixto (predeterminado), el puntero y el área de preparación se restablecen.

Restablecer Git duro

El propósito del restablecimiento completo de git es mover HEAD a la confirmación especificada. Eliminará todas las confirmaciones que sucedieron después de la confirmación especificada. Este comando cambiará el historial de confirmación y apuntará a la confirmación especificada.

En este ejemplo, agregaré tres archivos nuevos, los confirmaré y luego realizaré un restablecimiento completo.

Como puede ver en el siguiente comando, en este momento, no hay nada que confirmar.

$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.

(use "git push" to publish your local commits)

nothing to commit, working tree clean

Ahora, crearé 3 archivos y les agregaré algo de contenido.

$ vi file1.txt
$ vi file2.txt
$ vi file3.txt

Agregue estos archivos al repositorio existente.

$ git add file*

Cuando vuelva a ejecutar el comando de estado, reflejará los nuevos archivos que acabo de crear.

$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.

(use "git push" to publish your local commits)

Changes to be committed:

(use "git restore --staged <file>..." to unstage)

new file:
file1.txt

new file:
file2.txt

new file:
file3.txt

Antes de confirmar, déjame mostrarte que actualmente tengo un registro de 3 confirmaciones en Git.

$ git log --oneline
0db602e (HEAD -> master) one more commit
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

Ahora, me comprometeré con el repositorio.

$ git commit -m 'added 3 files'
[master d69950b] added 3 files
3 files changed, 3 insertions(+)
create mode 100644 file1.txt
create mode 100644 file2.txt
create mode 100644 file3.txt

Si hago ls-files, verá que se han agregado los nuevos archivos.

$ git ls-files
demo
dummyfile
newfile
file1.txt
file2.txt
file3.txt

Cuando ejecuto el comando de registro en git, tengo 4 confirmaciones y HEAD apunta a la última confirmación.

$ git log --oneline
d69950b (HEAD -> master) added 3 files
0db602e one more commit
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

Si voy y elimino el archivo 1.txt manualmente y hago un estado de git, mostrará el mensaje de que los cambios no están preparados para la confirmación.

$ git status
On branch master
Your branch is ahead of 'origin/master' by 3 commits.

(use "git push" to publish your local commits)

Changes not staged for commit:

(use "git add/rm <file>..." to update what will be committed)

(use "git restore <file>..." to discard changes in working directory)

deleted:
file1.txt

no changes added to commit (use "git add" and/or "git commit -a")

Ahora, ejecutaré el comando de restablecimiento completo.

$ git reset --hard
HEAD is now at d69950b added 3 files

Si vuelvo a verificar el estado, encontraré que no hay nada que confirmar y que el archivo que eliminé volvió al repositorio. La reversión ocurrió porque después de eliminar el archivo, no me comprometí, por lo que después de un restablecimiento completo, volvió al estado anterior.

$ git status
On branch master
Your branch is ahead of 'origin/master' by 3 commits.

(use "git push" to publish your local commits)

nothing to commit, working tree clean

Si reviso el registro de git, así es como se verá.

$ git log
commit d69950b7ea406a97499e07f9b28082db9db0b387 (HEAD -> master)
Author: mrgeek <[email protected]>
Date:
Mon May 17 19:53:31 2020 +0530

added 3 files

commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14
Author: mrgeek <[email protected]>
Date:
Mon May 17 01:04:13 2020 +0530

one more commit

commit 59c86c96a82589bad5ecba7668ad38aa684ab323
Author: mrgeek <[email protected]>
Date:
Mon May 17 00:54:53 2020 +0530

new commit

commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD)
Author: mrgeek <[email protected]>
Date:
Mon May 17 00:16:33 2020 +0530

test

El propósito del restablecimiento completo es apuntar a la confirmación especificada y actualizar el directorio de trabajo y el área de preparación. Déjame mostrarte un ejemplo más. Actualmente, la visualización de mis confirmaciones se ve a continuación:

Aquí, ejecutaré el comando con HEAD^, lo que significa que quiero restablecer la confirmación anterior (una confirmación posterior).

$ git reset --hard HEAD^
HEAD is now at 0db602e one more commit

Puede ver que el puntero de la cabeza ahora ha cambiado a 0db602e desde d69950b.

$ git log --oneline
0db602e (HEAD -> master) one more commit
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

Si revisa el registro, la confirmación de d69950b se ha ido y el encabezado ahora apunta a 0db602e SHA.

$ git log
commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14 (HEAD -> master)
Author: mrgeek <[email protected]>
Date:
Mon May 17 01:04:13 2020 +0530

one more commit

commit 59c86c96a82589bad5ecba7668ad38aa684ab323
Author: mrgeek <[email protected]>
Date:
Mon May 17 00:54:53 2020 +0530

new commit

commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD)
Author: mrgeek <[email protected]>
Date:
Mon May 17 00:16:33 2020 +0530

Test

Si ejecuta los archivos ls, puede ver que file1.txt, file2.txt y files3.txt ya no están en el repositorio porque esa confirmación y su archivo se eliminaron después del restablecimiento completo.

$ git ls-files
demo
dummyfile
newfile

Reinicio suave de Git

Del mismo modo, ahora te mostraré un ejemplo de reinicio por software. Considere, agregué los 3 archivos nuevamente como se mencionó anteriormente y los confirmé. El registro de git aparecerá como se muestra a continuación. Puede ver que ‘reinicio suave’ es mi última confirmación, y HEAD también apunta a eso.

$ git log --oneline
aa40085 (HEAD -> master) soft reset
0db602e one more commit
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

Los detalles de la confirmación en el registro se pueden ver usando el siguiente comando.

$ git log
commit aa400858aab3927e79116941c715749780a59fc9 (HEAD -> master)
Author: mrgeek <[email protected]>
Date:
Mon May 17 21:01:36 2020 +0530

soft reset

commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14
Author: mrgeek <[email protected]>
Date:
Mon May 17 01:04:13 2020 +0530

one more commit

commit 59c86c96a82589bad5ecba7668ad38aa684ab323
Author: mrgeek <[email protected]>
Date:
Mon May 17 00:54:53 2020 +0530

new commit

commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD)
Author: mrgeek <[email protected]>
Date:
Mon May 17 00:16:33 2020 +0530

test

Ahora, usando el restablecimiento parcial, quiero cambiar a una de las confirmaciones más antiguas con SHA 0db602e085a4d59cfa9393abac41ff5fd7afcb14

Para hacer eso, ejecutaré el siguiente comando. Debe pasar más de 6 caracteres iniciales de SHA, no se requiere el SHA completo.

$ git reset --soft 0db602e085a4

Ahora, cuando ejecuto el registro de git, puedo ver que HEAD se ha restablecido al compromiso que especifiqué.

$ git log
commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14 (HEAD -> master)
Author: mrgeek <[email protected]>
Date:
Mon May 17 01:04:13 2020 +0530

one more commit

commit 59c86c96a82589bad5ecba7668ad38aa684ab323
Author: mrgeek <[email protected]>
Date:
Mon May 17 00:54:53 2020 +0530

new commit

commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD)
Author: mrgeek <[email protected]>
Date:
Mon May 17 00:16:33 2020 +0530

test

Pero la diferencia aquí es que los archivos de la confirmación (aa400858aab3927e79116941c715749780a59fc9) donde agregué 3 archivos todavía están en mi directorio de trabajo. No se han borrado. Es por eso que debe usar un restablecimiento parcial en lugar de un restablecimiento completo. No hay riesgo de perder los archivos en el modo suave.

$ git ls-files
demo
dummyfile
file1.txt
file2.txt
file3.txt
newfile

Git Revertir

En Git, el comando revert se utiliza para realizar una operación de reversión, es decir, para revertir algunos cambios. Es similar al comando de reinicio, pero la única diferencia aquí es que realiza una nueva confirmación para volver a una confirmación en particular. En resumen, es justo decir que el comando git revert es una confirmación.

El comando de reversión de Git no elimina ningún dato mientras se realiza la operación de reversión.

Digamos que estoy agregando 3 archivos y realizando una operación de confirmación de git para el ejemplo de reversión.

$ git commit -m 'add 3 files again'
[master 812335d] add 3 files again
3 files changed, 3 insertions(+)
create mode 100644 file1.txt
create mode 100644 file2.txt
create mode 100644 file3.txt

El registro mostrará la nueva confirmación.

$ git log --oneline
812335d (HEAD -> master) add 3 files again
0db602e one more commit
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

Ahora me gustaría volver a uno de mis compromisos anteriores, digamos: «59c86c9 nuevo compromiso». Ejecutaría el siguiente comando.

$ git revert 59c86c9

Esto abrirá un archivo, encontrará los detalles de la confirmación a la que está tratando de revertir, y puede darle un nombre a su nueva confirmación aquí, y luego guardar y cerrar el archivo.

Revert "new commit"

This reverts commit 59c86c96a82589bad5ecba7668ad38aa684ab323.

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch master
# Your branch is ahead of 'origin/master' by 4 commits.
# (use "git push" to publish your local commits)
#
# Changes to be committed:
# modified: dummyfile

Después de guardar y cerrar el archivo, este es el resultado que obtendrá.

$ git revert 59c86c9
[master af72b7a] Revert "new commit"
1 file changed, 1 insertion(+), 1 deletion(-)

Ahora, para realizar los cambios necesarios, a diferencia de reset, revert ha realizado una nueva confirmación más. Si revisa el registro nuevamente, encontrará una nueva confirmación debido a la operación de reversión.

$ git log --oneline
af72b7a (HEAD -> master) Revert "new commit"
812335d add 3 files again
0db602e one more commit
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

El registro de Git tendrá todo el historial de confirmaciones. Si desea eliminar las confirmaciones del historial, revertir no es una buena opción, pero si desea mantener los cambios de confirmación en el historial, revertir es el comando adecuado en lugar de restablecer.

Git Rebase

En Git, rebase es la forma de mover o combinar confirmaciones de una rama sobre otra rama. Como desarrollador, no crearía mis funciones en la rama maestra en un escenario del mundo real. Trabajaría en mi propia rama (una ‘rama de funciones’), y cuando tenga algunas confirmaciones en mi rama de funciones con la función agregada, me gustaría moverla a la rama maestra.

Rebase a veces puede ser un poco confuso de entender porque es muy similar a una fusión. El objetivo de fusionar y reorganizar ambos es tomar las confirmaciones de mi rama de características y colocarlas en una rama maestra o en cualquier otra rama. Considere, tengo un gráfico que se ve así:

Suponga que está trabajando en un equipo con otros desarrolladores. En ese caso, puede imaginar que esto podría volverse realmente complejo donde hay muchos otros desarrolladores trabajando en diferentes ramas de funciones, y han estado fusionando múltiples cambios. Se vuelve confuso rastrear.

Entonces, aquí es donde la rebase va a ayudar. Esta vez, en lugar de hacer una combinación de git, haré una reorganización, donde quiero tomar mis dos compromisos de rama de función y moverlos a la rama maestra. Un rebase tomará todas mis confirmaciones de la rama de características y las moverá encima de las confirmaciones de la rama maestra. Entonces, detrás de escena, git está duplicando las confirmaciones de la rama de características en la rama maestra.

Este enfoque le dará un gráfico de línea recta limpio con todas las confirmaciones en una fila.

Hace que sea fácil rastrear qué confirmaciones fueron a dónde. Puedes imaginar que si estás en un equipo con muchos desarrolladores, todas las confirmaciones todavía están en una fila. Por lo tanto, es realmente fácil de seguir incluso si tiene muchas personas trabajando en el mismo proyecto al mismo tiempo.

Déjame mostrarte esto de manera práctica.

Así es como se ve mi rama maestra actualmente. Tiene 4 compromisos.

$ git log --oneline
812335d (HEAD -> master) add 3 files again
0db602e one more commit
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

Ejecutaré el siguiente comando para crear y cambiar a una nueva rama llamada función, y esta rama se creará a partir de la segunda confirmación, es decir, 59c86c9

(master)
$ git checkout -b feature 59c86c9
Switched to a new branch 'feature'

Si verifica el registro en la rama de funciones, solo tiene 2 confirmaciones provenientes del maestro (línea principal).

(feature)
$ git log --oneline
59c86c9 (HEAD -> feature) new commit
e2f44fc (origin/master, origin/HEAD) test

Crearé la característica 1 y la enviaré a la rama de características.

(feature)
$ vi feature1.txt

(feature)
$ git add .
The file will have its original line endings in your working directory

(feature)
$ git commit -m 'feature 1'
[feature c639e1b] feature 1
1 file changed, 1 insertion(+)
create mode 100644 feature1.txt

Crearé una función más, es decir, la función 2, en la rama de funciones y la confirmaré.

(feature)
$ vi feature2.txt

(feature)
$ git add .
The file will have its original line endings in your working directory

(feature)
$ git commit -m 'feature 2'
[feature 0f4db49] feature 2
1 file changed, 1 insertion(+)
create mode 100644 feature2.txt

Ahora, si revisa el registro de la rama de características, tiene dos confirmaciones nuevas, que ejecuté anteriormente.

(feature)
$ git log --oneline
0f4db49 (HEAD -> feature) feature 2
c639e1b feature 1
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

Ahora quiero agregar estas dos nuevas funciones a la rama maestra. Para eso, usaré el comando rebase. Desde la rama de características, me basaré contra la rama maestra. Lo que esto hará es volver a anclar mi rama de características contra los últimos cambios.

(feature)
$ git rebase master
Successfully rebased and updated refs/heads/feature.

Ahora voy a seguir adelante y verificar la rama maestra.

(feature)
$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 3 commits.

(use "git push" to publish your local commits)

Y finalmente, rebase la rama maestra contra mi rama de características. Esto tomará esos dos nuevos compromisos en mi rama de funciones y los reproducirá en la parte superior de mi rama maestra.

(master)
$ git rebase feature
Successfully rebased and updated refs/heads/master.

Ahora, si reviso el registro en la rama maestra, puedo ver que las dos confirmaciones de mi rama de características se han agregado a mi rama maestra con éxito.

(master)
$ git log --oneline
766c996 (HEAD -> master, feature) feature 2
c036a11 feature 1
812335d add 3 files again
0db602e one more commit
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

Eso fue todo sobre los comandos de reinicio, reversión y rebase en Git.

Conclusión

Eso fue todo sobre los comandos de reinicio, reversión y rebase en Git. Espero que esta guía paso a paso haya sido útil. Ahora, sabe cómo jugar con sus compromisos según la necesidad usando los comandos mencionados en el artículo.