Muy buenas lector. Este es el tercer post de la serie sobre Git, el famoso sistema de control de versiones desarrollado por Linus Torvalds en 2007, y que a día de hoy podemos decir que se ha convertido en el estándar de facto en su ámbito.
En el post anterior de esta serie te ofrecí una guía completa sobre las elementos y operaciones más importantes que debes conocer a la hora de trabajar con Git, desde la creación de repositorios, pasando por la publicación de commits, y terminando en la creación y merging de ramas. Esta guía la puedes ver como una extensión de la primera, pero en este caso pondré el foco en comandos algo más avanzados, pero igualmente útiles (aunque quizá no tan comunes).
Tabla de contenidos:
El Estado Detached HEAD

HEAD es una referencia simbólica de la rama en la que te encuentras en cada momento. Al ser una referencia simbólica, HEAD apunta, por defecto, a la referencia de la rama en cuestión, la cual, a su vez, apunta al último commit de la misma. Gracias a esto, cada vez que saltas a otra rama, HEAD siempre se situará al final de la misma.
Aún así, es posible mover esa referencia, para que en vez de apuntar a la rama (y por ende, al último commit de la misma), apunte a un commit concreto. Esto puede ser útil, por ejemplo, para crear una nueva rama desde un commit anterior.
Desplazar el Puntero HEAD en un Commit Anterior
Para situar HEAD en un commit concreto, tan solo debes ejecutar el siguiente comando, sustituyendo <hash-commit>
por el número de hash del commit en el que quieres situarte (el hash del commit lo puedes obtener fácilmente con tan solo listar los diferentes commits de la rama actual, con el comando git log
).
git checkout <hash-commit>
Cuando sitúas la referencia HEAD en un commit anterior al último de la rama, automáticamente Git pasa a encontrarse en un estado denominado «detached HEAD». Esto lo puedes observar fácilmente, por ejemplo, cuando realizas un git status
.
Crear Nueva Rama en Detached HEAD
Una vez te encuentres en el estado «detached HEAD», una de las cosas que podrías hacer es, por ejemplo, crear una nueva rama, tomando como referencia el commit en el que te encuentres ahora (que ya no será el último de la rama). De este modo, esta nueva rama será una bifuración de la rama actual, pero naciendo un commit anterior al último.
git branch <nombre de la rama>
Con el comando anterior solo habrás creado la rama, pero la referencia HEAD no habrá cambiado aún, seguirá en «detached HEAD», y en la misma rama actual.
Salir del Estado Detached HEAD
Para salir del estado «detached HEAD» y saltar en otra rama (o en el último commit de la misma) tan solo debes hacer un salto de rama con el comando que tienes a continuación, sustituyendo <nombre de la rama>
por el nombre de la rama en la que te quieras situar, ya sea la rama actual, la nueva que hayas podido crear desde el estado «detached HEAD», o cualquir otra.
git switch <nombre de la rama>
Al saltar a una rama, automaticamente saldrás del estado «detached HEAD», significando que la referencia HEAD volverá a apuntar a la referencia de la rama (y por ende, al último commit de la misma).
Stash

En caso de que tengas cambios pendientes a nivel de directorio de trabajo, y quieras saltar a otra rama sin publicarlos aún dentro de ningún commit, Git te alertará de ello, recomendándote que los publiques con un commit.
En caso de que no los quieras publicar aún, Git permite capturar estos cambios temporalmente en un área denominada stash, y quitarlos del directorio de trabajo, para poder saltar a otra rama sin llevarlos contigo.
Guardar Cambios en Stash
Para limpiar el directorio de trabajo de los últimos cambios no publicados, y capturarlos en el área de stash, tan solo debes ejecutar el siguiente comando. Haciendo esto, esos cambios dejarán de estar disponibles para entrar en el próximo commit, por lo que podrás centrarte en otras modificaciones, y utilizarlos en un futuro cuando los necesites.
git stash
Utilizar los Cambios en Stash
Llegado el momento, cuando quieras utilizar los cambios que tengas guardados en reserva, puedes quitarlos del stash para que vuelvan a figurar a nivel de directorio de trabajo. Haciendo esto, dichos cambios volverán a estar disponibles para que puedan volver a quedar incluidos en el próximo commit que publiques.
git stash pop
Este ciclo puedes volver a repetirlo tantas veces como necesites.
Restaurar, Resetear y Revertir Cambios
Cuando trabajas extensivamente con Git, es muy común que, en ocasiones, cometas errores, como añadir los ficheros equivocados en el área de staging, o publicar un commit en la rama equivocada. En estos casos, es imprescindible disponer de mecanismos para poder deshacer estos errores.
Para ello, Git ofrece tres mecanismos distintos, denominados restore, reset y revert. Los tres te permitirán deshacer cambios, pero de formas diferentes, por lo que deberás ser tu mismo el que sepa identificar cuál de ellos necesitas utilizar, en función de la situación en la que te encuentres.
- Restore te permite descartar los cambios realizados sobre un archivo concreto del directorio de trabajo, y restaurarlo a la versión en la que se encuentra a nivel de reporitorio.
- Reset te permite establecer el repositorio a la versión establecida por un commit anterior. Esto es como ir atrás en el tiempo, de modo que todos los cambios publicados en los commits posteriores serán descartados, y este commit será ahora el último. Los cambios realizados en los ficheros en el transcurso de esos commits, sin embargo, se seguirán manteniendo a nivel del directorio de trabajo.
- Revert es parecido al reset, en el sentido de que permite deshacer los cambios publicados en una rama desde un determinado commit en adelante, pero lo que hace para lograrlo es publicar un commit nuevo, que se encarga especificamente de revertir cada uno de los cambios.
Git Restore
En caso de que hayas añadido ficheros con cambios en el área de staging, y más adelante de des cuenta de que uno de los ficheros contiene cambios equivocados y necesitas deshacerlos, puedes restaurar el fichero a la misma versión en la que figura a nivel de repositorio (concretamente, en la referencia HEAD establecida en ese momento), puedes realizar una operación de restore utilizando el siguiente comando, sustituyendo <archivo>
por el nombre del archivo en cuestión.
git restore <archivo>
Otra opción, si quieres seguir trabajando con los cambios en dicho fichero, pero evitar que entren en el repositorio con la publicación del próximo commit, es simplemente quitar el fichero del área de staging. Esto puedes hacerlo con la misma operación de restore, pero añadiendo el parámetro --staged
en el comando.
git restore --staged <archivo>
Git Reset
Si has publicado una serie de commits en la rama equivocada, y deseas quitarlos de la misma, puedes utilizar una operación de reset, ejecutando el siguiente comando, y sustituyendo <hash-commit> por el hash del último commit que quieres que figure en dicha rama. Esto quitará de la rama todos los commits publicados a posteriori, pero manteniendo los cambios asociados a los mismos a nivel de directorio de trabajo (para, por ejemplo, poder publicarlos en otra rama).
git reset <hash-commit>
De forma complementaria a lo anterior, hay otra variante de reset, denominado hard reset, con la que podrás, además de eliminar los commits posteriores al que hayas indicado, eliminar también los cambios asociados a ellos a nivel de directorio de trabajo (esto sirve para los casos en los que realmente quieras eliminar los cambios).
git reset --hard <hash-commit>
Git Revert
Si te encuentras en la misma situación anterior de haber publicado una serie de commits que deseas eliminar de la rama actual, otra forma de hacerlo es mediante la operación de revert, utilizando el comando que tienes a continuación, y sustituyendo <hash-commit>
por el hash del último commit que quieres que figure en dicha rama. Con esto, lo que harás, es, en vez de quitar los commits, publicar un nuevo commit que se encargue de revertir los cambios en los ficheros.
git revert <hash-commit>
Con esta operativa, el resultado final es el mismo que en el caso del reset, pero dejando constancia en todo momento de los commits. Esto convierte a esta operación en la más recomendable para los casos en los que te interesa dejar una trazabilidad de todo lo que se ha hecho (sobretodo cuando trabajas de forma colaborativa).
Rebase

A la hora de unir dos ramas, aparte de la operación de merging ya vista, Git ofrece otra operación denominada rebase, que lo que hace es, estando situado en una rama concreta, copiar todos los commits de la rama que queremos unir, y añadirlos, uno a uno, al final de la rama en la que te encuentres situado, terminando en una estructura lineal.
Uno de los casos de uso más típicos de la operación de rebase es cuando estas trabajando en una rama secundaria, pero la rama principal está muy activa, y necesitas estar fusionando constantemente todo el trabajo nuevo de la rama principal con la rama secundaria. En este caso, para evitar que la rama secundaria termine llena de commits de unión, puedes utilizar la operación de rebase, como te explicaré a continuación.
Dicho esto, la operación de rebase es una de las operaciones mas críticas de Git, y no te recomendaría utilizarla a menos que sepas muy bien lo que haces. Además, en caso de trabajar en un proyecto colaborativo, y hayas subido confirmaciones en el repositorio remoto, no se recomienda en ningún caso hacer un rebase, a menos de que estés muy seguro que nadie esté utilizando estas confirmaciones.
Hacer un Rebase para Unir dos Ramas
Si estás situado en una rama concreta, y quieres unir otra rama secundaria a ella, puedes realizar un rebase con el comando que te dejo a continuación, sustituyendo <nombre de la rama>
con el nombre de la rama secundaria que quieres unir a la actual. Con esta operación, todos los commits de la rama secundaria se añadirán justo a continuación de la rama actual.
git rebase <nombre de la rama>
Al igual que en el caso del merge, la operación de rebase puede generar conflictos. En este caso, si la operación ha empezado pero no a podido terminar, deberás primero resolver los conflictos en los archivos afectados, agregarlos al área de staging con un git add
, y, finalmente, en lugar de publicar un commit, dar la orden de continuar el rebase
, a través del siguiente comando:
git rebase --continue
Hacer un Rebase para Reorganizar Commits
Además de como método para unir dos ramas, la operación de rebase también puede utilizarse como herramienta de limpieza. Desde el modo interactivo del rebase podrás editar commits anteriores, añadir archivos a los mismos, descartar commits, etc. Para ello, puedes utilizar el siguiente comando, sustituyendo <n>
por el número de commits anteriores a la referencia HEAD actual, y que te interesa editar.
git rebase -i HEAD~<n>
Continuación
En esta guía, como has podido ver, me he centrado es operaciones de Git bastante más avanzadas que en la guía previa. Todas ellas pueden llegar a ser sumamente útiles llegado el momento, pero debes tener algo más de cuidado a la hora de utilizarlas.
Si justo estás empezando con Git, y, por la magia de Google, has ido a parar en esta página, te recomiendo que no dejes de echar un ojo al resto de posts de esta serie, empezando desde el primero.
- Git (I). Introducción
- Git (II). Instalación y configuración inicial.
- Git (III). Guía completa
- Git (IV). Guía avanzada
¡Un saludo y hasta la próxima!