¿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 git
todo 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 git
es útil, obtener la teoría casi nunca es suficiente. ¿Cómo aplicamos nuestro conocimiento de git
los 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 reset
comando. Llegaremos a entender lo que git reset
hace 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, git
no 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 git
la 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 status
pone 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 commit
también hace que la rama actual master
apunte al objeto de confirmación recién creado .

El funcionamiento interno de git reset
Me gusta pensar git reset
en 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
, --mixed
o --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 reset
actualiza lo que HEAD
apunte. Porque git reset --hard HEAD~1
moverá lo que HEAD
apunta (en el ejemplo anterior master
) a HEAD~1
. Si — -soft
se usa la bandera, se git reset
detiene allí.
Continuando con nuestro ejemplo anterior, HEAD
apuntará commit 2
y, por new_file.txt
lo 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 git
no se detendrá después de actualizar los HEAD
puntos 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 HEAD
punto a ( master
) a HEAD~1
, así como actualizar el índice a (el ya actualizado) HEAD
, git
avanzará 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 reset
funciona, ¡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 master
rama, 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 new
rama apunta a nuestra confirmación recientemente agregada. En segundo lugar, master
apunta a la confirmación anterior. En tercer lugar, HEAD
apunta a new
.
Podemos llegar al estado deseado mediante tres sencillos pasos:
Primero, haga que la new
rama 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, master
señ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, HEAD
señ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 master
rama, 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 existing
rama. Dado que master
actualmente lo apunta, simplemente podemos pedir git
tomar la confirmación reciente de la master
rama y aplicarla a la existing
rama así:
git checkout existing
- cambiar aexisting
ramagit cherry-pick master
- aplicando la última confirmación en lamaster
rama a la rama actual (existing
)
Ahora, llegamos al siguiente estado:

Ahora solo tenemos que master
señalar la confirmación anterior, en lugar de la última. Para eso podemos:
git checkout master
- cambie la rama activa amaster
otra 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 reset
funciona, y aclaró sus tres modos de funcionamiento - --soft
, --mixed
y --hard
.
Luego aplicamos nuestro conocimiento git reset
para resolver algunos problemas de la vida real git
.
Al comprender la forma en que git
opera, podemos abordar con confianza todo tipo de escenarios y también apreciar la belleza de esta herramienta.
En publicaciones futuras, cubriremos git
comandos 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)