Explicación de Git Reset: cómo salvar el día con el comando Reset

¿Te suena familiar? "¡Ayuda! ¡Me comprometí con la rama equivocada! " "Sucedió de nuevo ... ¿Dónde está mi compromiso?"

Bueno, he estado allí tantas veces. Alguien dice mi nombre pidiendo ayuda cuando algo sale mal git. Y ha sucedido no solo cuando estaba enseñando a los estudiantes, sino también mientras trabajaba con desarrolladores experimentados.

Con el tiempo, me convertí en “el tipo Git”.

Usamos gittodo el tiempo y, por lo general, nos ayuda a hacer el trabajo. Pero a veces, y con mucha más frecuencia de lo que quisiéramos, las cosas salen mal.

Quizás nos hemos comprometido con la rama equivocada. Quizás perdimos algún código que escribimos. Quizás cometimos algo que no queríamos.

Hay muchos recursos en línea sobre git, y algunos de ellos (como este) realmente se enfocan en lo que sucede en estos escenarios no deseados.

Pero siempre sentí que a estos recursos les faltaba el " por qué". Cuando se le proporciona un conjunto de comandos, ¿qué hace cada comando? ¿Y cómo llegó a estos comandos en primer lugar? ?

En una publicación anterior, proporcioné una introducción visual a los componentes internos de Git. Si bien comprender los aspectos internos de gites útil, obtener la teoría casi nunca es suficiente. ¿Cómo aplicamos nuestro conocimiento de gitlos aspectos internos y lo usamos para solucionar los problemas en los que nos hemos metido?

En esta publicación, me gustaría cerrar esta brecha y desarrollar el git resetcomando. Llegaremos a entender lo que git resethace detrás de escena, y luego aplicaremos este conocimiento para resolver varios escenarios. ?

Elementos comunes: directorio, índice y repositorio de trabajo

Para comprender los mecanismos internos de git reset, es importante comprender el proceso de registrar los cambios internos git . Específicamente, me refiero al directorio de trabajo , el índice y el repositorio.

Si está seguro de estos términos, no dude en pasar a la siguiente sección. Si desea una explicación aún más profunda, consulte esta publicación anterior.

Cuando trabajamos en nuestro código fuente, trabajamos desde un directorio de trabajo:  cualquier directorio de nuestro sistema de archivos que tenga un repositorio asociado. Contiene las carpetas y archivos de nuestro proyecto, y también un directorio llamado .git.

Después de realizar algunos cambios, queremos registrarlos en nuestro repositorio . Un repositorio ( repo para abreviar) es una colección de confirmaciones , cada una de las cuales es un archivo de cómo se veía el árbol de trabajo del proyecto en una fecha pasada, ya sea en nuestra máquina o en la de otra persona.

Creemos un archivo en el directorio de trabajo y ejecutemos git status:

Sin embargo, gitno confirma los cambios del árbol de trabajo directamente en el repositorio .

En su lugar, los cambios se registran primero en algo llamado índice o área de preparación . Ambos términos se refieren a lo mismo y se utilizan a menudo en gitla documentación de. Usaremos estos términos indistintamente a lo largo de esta publicación.

Cuando usamos git add, agregamos archivos (o cambios dentro de los archivos) al área de preparación . Usemos este comando en el archivo que creamos anteriormente:

Como se git statuspone de manifiesto, nuestro archivo se organizó (y listo “estar comprometido”). Sin embargo, no forma parte de ningún compromiso . En otras palabras, ahora está en el directorio de trabajo , así como en el índice , pero no en el repositorio .

Luego, cuando usamos git commit, creamos una confirmación basada en el estado del índice . Entonces, la nueva confirmación (confirmación 3 en el ejemplo siguiente) incluirá el archivo agregado al índice de antemano.

En otras palabras, el directorio de trabajo tiene exactamente el mismo estado que el índice y el repositorio .

El comando git committambién hace que la rama actual masterapunte al objeto de confirmación recién creado .

El funcionamiento interno de git reset

Me gusta pensar git reseten un comando que invierte el proceso descrito anteriormente (introduciendo un cambio en el directorio de trabajo , agregándolo al índice y luego comprometiéndolo en el repositorio ).

Git reset tiene tres modos de funcionamiento -  --soft, --mixedo --hard. Los veo como tres etapas:

  • Etapa 1 - actualización HEAD - git reset --soft
  • Etapa 2 - índice de actualización  - git reset --mixed
  • Etapa 3 - actualización del directorio de trabajo  - git reset --hard

Etapa 1: actualización HEAD( git reset --soft)

Primero, git resetactualiza lo que HEADapunte. Porque git reset --hard HEAD~1moverá lo que HEADapunta (en el ejemplo anterior master) a HEAD~1. Si — -softse usa la  bandera, se git resetdetiene allí.

Continuando con nuestro ejemplo anterior, HEADapuntará commit 2y, por new_file.txtlo tanto , no será parte del árbol de la confirmación actual. Sin embargo, será parte del índice y del directorio de trabajo .

Al mirar git status, podemos ver que el archivo está realmente preparado pero no confirmado:

En otras palabras, revertimos el proceso a la etapa en la que usamos git add, pero aún no lo usamos git commit.

Etapa 2: actualizar el índice a HEAD ( git reset --mixed)

Si usamos git reset --mixed HEAD~1, entonces gitno se detendrá después de actualizar los HEADpuntos a ( master) a HEAD~1. También actualizará el índice a (el ya actualizado) HEAD.

En nuestro ejemplo, eso significa que el índice tendrá el mismo estado que el compromiso 2 :

Así que volvimos el proceso a la etapa anterior a su uso git add : el archivo recién creado ahora es parte del directorio de trabajo, pero el índice y el repositorio no lo son.

Etapa 3: actualice el directorio de trabajo a index ( git reset --hard)

Al usar git reset — hard HEAD~1, después de actualizar cualquier HEADpunto a ( master) a HEAD~1, así como actualizar el índice a (el ya actualizado) HEAD, gitavanzará y actualizará el directorio de trabajo para que se parezca al índice .

En nuestro ejemplo, eso significa que el directorio de trabajo tendrá el mismo estado que el índice, que ya tiene el mismo estado que la confirmación 2 :

De hecho, revertimos todo el proceso incluso antes de crear my_file.txt.

Aplicar nuestro conocimiento a escenarios del mundo real

Ahora que entendemos cómo git resetfunciona, ¡apliquemos este conocimiento para salvar nuestro día! ?

1. ¡Vaya! Cometí algo por error.

Consideremos el siguiente escenario. Creamos un archivo con la cadena This is very importnt, lo organizamos y lo comprometimos.

Y luego ... ¡Ups! Nos dimos cuenta de que teníamos un error de escritura. ?

Bueno, ahora sabemos que podemos resolverlo fácilmente. Podemos revertir nuestra última confirmación y devolver el archivo al directorio de trabajo usando git reset --mixed HEAD~1. Ahora, podemos editar el contenido de nuestro archivo, prepararlo y enviarlo nuevamente.

Sugerencia: en este caso específico, también podríamos usar git commit --amend, como se describe aquí.

2. ¡Vaya! Envié algo a la rama incorrecta y lo necesito en una nueva rama

Todos hemos estado allí. Hicimos un trabajo y luego lo comprometimos ...

Oh no, nos comprometimos con la masterrama, aunque deberíamos haber creado una nueva rama y luego emitir una solicitud de extracción. ?

En esta etapa, me resulta útil visualizar el estado en el que nos encontramos y adónde nos gustaría llegar:

En realidad, hay tres cambios entre el estado actual y el deseado.

Primero, la newrama apunta a nuestra confirmación recientemente agregada. En segundo lugar, masterapunta a la confirmación anterior. En tercer lugar, HEADapunta a new.

Podemos llegar al estado deseado mediante tres sencillos pasos:

Primero, haga que la newrama apunte a la confirmación agregada recientemente; esto se puede lograr simplemente usando git branch new. Por tanto, llegamos al siguiente estado:

En segundo lugar, masterseñale la confirmación anterior (en otras palabras, a HEAD~1). Podemos hacer eso usando git reset --hard HEAD~1. Por tanto, llegamos al siguiente estado:

Por último, nos gustaría estar en la rama new, es decir, HEADseñalar new. Esto se logra fácilmente realizando git checkout new.

Considerándolo todo:

  • git branch new
  • git reset --hard HEAD~1
  • git checkout new

3. ¡Vaya! Envié algo a la rama incorrecta, y lo necesito en otra rama ya existente

En este caso, seguimos los mismos pasos que en el escenario anterior: hicimos un trabajo y luego lo comprometimos ...

Oh no, nos comprometimos con la masterrama, aunque deberíamos habernos comprometido con otra rama que ya existe. ?

Volvamos a nuestro tablero de dibujo:

Nuevamente, podemos ver que hay algunas diferencias aquí.

Primero, necesitamos que la confirmación más reciente esté en la existingrama. Dado que masteractualmente lo apunta, simplemente podemos pedir gittomar la confirmación reciente de la masterrama y aplicarla a la existingrama así:

  • git checkout existing - cambiar a existingrama
  • git cherry-pick master - aplicando la última confirmación en la masterrama a la rama actual ( existing)

Ahora, llegamos al siguiente estado:

Ahora solo tenemos que masterseñalar la confirmación anterior, en lugar de la última. Para eso podemos:

  • git checkout master - cambie la rama activa a masterotra vez.
  • git reset --hard HEAD~1 - ahora estamos de vuelta en la sucursal original.

Y hemos alcanzado nuestro estado deseado:

Resumen

En este post, hemos aprendido cómo git resetfunciona, y aclaró sus tres modos de funcionamiento -  --soft, --mixedy --hard.

Luego aplicamos nuestro conocimiento git resetpara resolver algunos problemas de la vida real git.

Al comprender la forma en que gitopera, podemos abordar con confianza todo tipo de escenarios y también apreciar la belleza de esta herramienta.

En publicaciones futuras, cubriremos gitcomandos adicionales y cómo pueden ayudarnos a resolver todo tipo de situaciones no deseadas.

Omer Rosenbaum , director de tecnología de Swimm . Experto en formación cibernética y fundador de Checkpoint Security Academy. Autor de Computer Networks (en hebreo) . Visite Mi canal de YouTube .

Recursos adicionales

  • Breve serie de Git Internals en YouTube
  • Una introducción visualizada a los componentes internos de Git: objetos y ramas
  • Obteniendo Hardcore - Creando un Repo desde cero
  • Herramientas de Git - Restablecer desmitificado (de Pro Git Book)