Si quieres desarrollar profesionalmente debes entender muy bien cómo funciona Git. No basta con utilizar los comandos básicos (git add, commit, push y pull request). Vamos a entender, de verdad, cómo funciona el flujo de trabajo de un repositorio, qué hacer cuando nos conectamos a un servidor remoto y encontramos conflictos para trabajar en equipo.
Seamos honestos, trabajar con Git es un poco aterrador, ¿verdad? Igual que hacer Pull Requests en GitHub o Merge Requests en GitLab. Pero también sabemos que basta con conocerlos y dominarlos para que convertirlos en nuestros mejores aliados.
Todo esto, de principio a fin, lo puedes aprender en el Curso de Git y Github de Platzi. También esta el Curso de DevOps con Gitlab para aprender a trabajar con pruebas, integraciones y entornos de trabajo automatizados. Te recomiendo ambos cursos.
Voy a mostrarte algunos comandos oscuros de Git que nos pueden salvar la vida. Literalmente. También vamos a repasar un poco el flujo de trabajo de nuestro proyecto y las etapas por las que deben pasar nuestros archivos.
¡Empecemos!
Así como el agua puede ser líquida, sólida o gaseosa, nuestros archivos pueden vivir y moverse entre diferentes estados cuando nuestro proyecto está conectado con Git.
Recuerda que podemos ver en qué estado se encuentran nuestros archivos usando el comando
git status
. Cada archivo puede estar en un estado diferente.
Comencemos por los estados normales, esos que son independientes de nuestro trabajo con otras ramas o servidores remotos:
Untracked: Son archivos que NO viven dentro de Git, solo en nuestro disco duro. Si nunca antes fueron incluidos en el repositorio de Git (por comandos como git add
), el sistema de control de versiones no tiene forma de guardar los registros de cambios de este archivo. Pero si estos archivos alguna vez fueron incluidos dentro de Git y fueron removidos por comandos como git rm
, entonces Git debe tener registros antiguos sobre estos archivos.
Todos los archivos comienzan en estado Untracked...
Mover un archivo de cualquier estado a Untracked
(pero conservando el archivo en el disco duro)
git rm --cached archivito
Remover un archivo de cualquier estado y del disco duro podremos recuperarlo si el archivo tiene historial dentro de Git,
# o sea, si alguna vez ha estado en un commit
git rm --force archivito
Staged: El Staging es un estado de preparación antes de hacer commit. Aquí guardamos las actualizaciones y nuevas incorporaciones de archivos al proyecto con el comando git add
. Son archivos en el área de Staging. Viven dentro de Git, tienen historial de cambios y sus últimas actualizaciones están listas para guardarse definitivamente en un nuevo commit cuando hagamos git commit
.
# Mover un archivo al área de Staging
git add archivito
# Mover TODOS los archivos al área de Staging# (excepto por los nombres del archivo .gitignore)
git add -A
git add .
Unstaged: También podemos entender este estado como “Tracked pero Unstaged”. Los archivos en este estado sí tienen historial de cambios en Git… Pero sus últimos cambios todavía no han pasado al área de Staging ni mucho menos han sido guardados en un nuevo commit. Todos los archivos entran a este estado luego de ser editados.
Recuerda que un mismo archivo puede tener ambos estados, Untracked y Unstaged, y eso lo resolvemos volviendo a ejecutar el comando git add para traer al Staging los cambios restantes. También podemos devolver un archivo del estado Staged a Unstaged con el comando git reset HEAD
.
# Todos los archivos entran a este estado luego de# ser editados y antes de ser enviados a Staging…# Mover un archivo de Staged a Unstaged:
git reset HEAD archivo
# Mover TODOS los archivos de estado Staged# a Unstaged:
git reset HEAD
Tracked: Son los archivos que viven dentro de Git y no tienen cambios pendientes. Sus últimas actualizaciones han sido guardadas en el repositorio y, a menos que se borren los últimos commits, su historial de cambios está disponible.
Los archivos solo pueden pasar a este estado si antes estuvieron en el área de Staging. Todos los archivos en estado Staged pasan al estado tracked con el comando git commit
. También podemos pasar los archivos al área Staged e inmediatamente al estado Tracked con el comando git commit -a
.
# Solo podemos hacer commit de archivos Staged…# Commit de archivos en el área de Staging:
git commit -m “Mensaje del commit”
# Pasar archivos al área de Staging y hacer commit# en un solo comando:
git commit -am “Mensaje del commit”
Recuerda que puedes ver los cambios entre los archivos Tracked y Staged con el comando
git diff
y que puedes volver a un commit anterior con el comandogit checkout id-del-commit
.
Al trabajar con ramas diferentes puede que nos encontremos con estos nuevos estados:
Unmerged: Es un estado especial, algo parecido a un estado intermedio entre Untracked y Unstaged. Solo debemos resolver los conflictos (editando nuestros archivos), hacer git add
para mandar los cambios a Staging y git commit
para guardar los cambios del merge en el repositorio.
Stashed: Es un estado que nos ayuda a guardar los cambios en Staging para poder cambiar sin perder el trabajo que todavía no guardamos en un commit. Nos permite cambiar de ramas, hacer cambios, trabajar en otras cosas y, más adelante, retomar el trabajo con los archivos que teníamos en Staging pero que podemos recuperar ya que los guardamos en el Stash.
# Guardar los cambios del Staging en Stash:
git stash
# Retomar los últimos cambios guardados en Stash, recuerda# que pueden generar conflictos que se resuelven igual que# los conflictos de git merge
git stash pop
# Ver la lista de cambios guardados en Stash:
git stash list
# Retomar los cambios de una posición específica del Stash:
git stash apply stash@{x}
# Borrar una posición específica del Stash:
git stash drop stash@{x}
# Aplicar los cambios del Stash a una rama:
git stash branch nombre_de_la_rama
Por último, recuerda que tenemos muchos otros estados cuando trabajamos con repositorios remotos (si, repositorios, en plural). Podemos tener más de un repositorio remoto, por ejemplo, cuando hacemos fork de un proyecto open source, enviamos commits (hacemos git push) al repositorio fork y desde GitHub enviamos pull request al proyecto “original”.
¿Con cuantos repositorios diferentes podemos trabajar?
¿Cuantos estados diferentes nos podemos encontrar cuando trabajamos con todos los repositorios juntos? ¿Como se solucionan los conflictos?
Todo está bien si:
Solo debes hacer git push
para mandar los últimos cambios al repositorio remoto.
Solo debes hacer git push
para mandar los últimos cambios al repositorio remoto fork y, cuando todo esté listo, mandas un pull request al repositorio remoto original.
Solo debes hacer git pull
(git fetch
y git merge
) para traer los últimos cambios del repositorio remoto. De hecho, siempre es muy buena práctica hacer pull antes de intentar hacer push o empezar a trabajar.
Tienes dos opciones:
origin
al repositorio fork y upstream
al repositorio original. Esto significa que puedes hacer pull del repositorio remoto original y push al repositorio remoto.También recuerda que pueden haber ramas diferentes en cada repositorio y debemos seguir el mismo proceso para cada una.
Vamos a ver algunos ejemplos y conflictos de la vida real que pueden sucedernos pero, en vez de alarmarnos, debemos aprender de ellos para convertirnos en mejores profesionales.
¿Que haces? Puedes guardar los cambios de los archivos en alguna parte, revertir el commit y volver a empezar. Por supuesto que puedes, pero hay una forma aún más fácil.
Solo debes volver a hacer commit pero esta vez usando el flag --amend
para indicarle a GIT que quieres aplicar los cambios sobre el commit anterior.
Por ejemplo:
git commit -m “Arreglado el bug #12”# Wait, ¡era el #13!
git commit --amend -m “Arreglado el bug #13”
¿Que haces? Puedes hacer los cambios en un nuevo commit, por supuesto, pero esa no es la idea. Este commit estaba destinado a hacer eso que dijiste que debía hacer. Por lo tanto, debes solucionarlo, no solo hacer otro commit.
En realidad, es muy fácil. Solo Haz los cambios que faltaban, luego los añades al staging area (sí, con git add) y usas git commit pero con los flags --amend
y --no-edit
para aplicar los nuevos cambios al último commit y dejando que el mensaje del commit sea el mismo de antes.
git commit -m “Ahora el carro es rojo”
# Wait, ¡todavía es azul! SHAME...# Hago los cambios para que el carro sea rojo
git add archivo-del-carrito
git commit --amend --no-edit # Yei!!
Recuerda que, aunque el flag --amend nos permite trabajar y hacer correcciones “sobre el mismo commit”, en realidad, estamos creando un nuevo commit y ubicándolo en la línea de tiempo en lugar de los commits donde nos equivocamos.
Esto significa que si trabajamos con alguien más y esas personas trabajan sobre el mismo commit que acabas de corregir con --amend, van a tener problemas y conflictos. Cada uno tendrá historiales diferentes.
Usa --amend con responsabilidad. De preferencia, usa `–amend en los cambios que solo te afectan a ti (en local). Si el error ya llego al resto del equipo es mejor resolver las fallas con ramas y merges.
¿Que haces? Lo admito: Hacer commit del trabajo que no está listo (con un mensaje explicando que no está listo) y cambiando de rama con mucho dolor en mi corazón por dañar la lista de mensaje bonitos de mi proyecto. Podemos hacer eso, por supuesto que podemos. Pero existe el comando git stash
que puede guardar nuestros cambios prematuros para retomarlos en otro momento.
Ya te explique como funciona git stash
. Vamos con un ejemplo:
# El carrito azul está quedando bonito. ¡AY! ¡Tengo un# error gravísimo y debo solucionarlo inmediatamente!# Pero… ¿qué hago con los cambios que no están listos
para hacer commit?
# Batman: ¡No se diga más!
git stash
git checkout -b fix-issue-130
# Juan todo tonto que no sabe para qué sirve git stash:# AAAHHHH NOOO MIS CAMBIOOS NOOOO!!!# Juan arregla el bugsito pero está todo triste porque# no ha tomado el curso de Git en Platzi y piensa que# sus cambios se perdieron para siempre…# Batman:
git stash pop
# Juan viendo que sus avances no se perdieron:# :O____# Batman: I’m Batman.
¿Que haces? Podemos entrar en pánico, por supuesto que puedes. Pero existe un comando que guarda todos nuestros movimientos, los commits que hemos creado, las ramas por donde nos hemos movido, todo lo podemos encontrar con git reflog
.
Recuerda que este comando es muy diferente de git log
que solo nos muestra los commits de la rama donde nos encontramos. Reflog nos muestra todo el historial, incluso, si las ramas donde trabajamos han sido borradas.
# Juan: El carro es rojo pero yo lo quiero azul... Voy a# crear una nueva rama para probar cómo se vé…
git checkout -b prueba-carro-azul
# El Jefe llega 5 min después: Juan, antes de hacer# cualquier otra cosa quiero que arregles el bug #131...# Juan: ¡Sí señor! Hmm… Esto es demorado. Creo que # voy a borrar la rama `prueba-carro-azul` para solo# enfocarme en el bugsito del jefe…# El tonto Juan borra la rama `prueba-carro-azul` y# arregla el bug que le dijo el jefe… Por supuesto, Juan# no recordaba que existía `git stash`... Juan es muy# novato y debería tomar el curso de Git en Platzi...# Juan después de arreglar el bugsito del jefe:# Hmm… Creo que quiero probar con el carrito azul# pero ya borre la rama con los cambios… ¿Y ahora?# Batman: ¡No se diga más!
git reflog
# … batman busca y copia el id del último commit de la# rama `prueba-carrito-azul` y lo pega al final de:
git checkout -b carrito-azul-segundo-intento id-del-commit
# Juan: :O____# Batman: I’m Batman.
El más oscuro de todos los comandos creados por todos los programas creados hasta el dia de hoy. Cada vez que lo usas muere un conejito. De hecho, como si no fuera suficiente, ese conejito va hasta tu casa y te atormentará el resto de días de tu vida recordándote que fuiste una mala persona y por tu culpa murió un conejito.
Nunca lo uses. Nunca lo menciones. Tu no sabes que existe ni mucho menos que yo te lo conte.
Este comando lo menciono por lo contrario; para que nunca lo uses. Por favor, aprende a convivir (en vez de luchar) con los conflictos. Están hechos por y para ti. Solo debemos aprender los comandos y los flujos de trabajo para solucionarlos.
En vez de esto, puedes (entre algunas otras formas de resolver este problema) usar diferentes ramas, generar conflictos, resolver los conflictos y, finalmente, hacer push a nuestro repositorio sin necesidad del flag --force.
Branch en Git: Qué es y cómo hacer ramas.
Todos estos comandos, desde el comienzo con git init hasta la mejor forma de resolver conflictos entre archivos, los puedes aprender en el Curso de Git y GitHub de Platzi. GitHub y GitLab (y Bitbucket o la plataforma que te guste) son muy poderosos pero solo sacaremos su máximo potencial si entendemos, de verdad, cómo funciona GIT. Además, GitLab tiene herramientas de DevOps muy poderosas que vale la pena aprender una vez tenemos las bases sólidas de Git.
#NuncaParesDeAprender y #NuncaParesDeAprenderGIT 🤓 💚
Un deporte extremo:
git rm -r --force * —> ves el diablo
git reset hard --hard [hash] —> ves una luz
*git reset --hard [hash] — ups —
😂
Hahaha lo hice cuando aprendí git
😄
Nadie:
De verdad, NADIE:
Batman: I’m Batman.
Gracias por esos Errores de noobs!
Entonces este post también te puede interesar: https://platzi.com/blog/10-errores-programador-novato/. 😉
Dios, es la explicación definitiva de los estados de Git y los distintos comandos para cambiar entre ellos. Me ha ayudado a entender cosas que no terminaba de pillar. ¡Gracias!
muy bueno este blog post…
Genial, sobretodo la parte de los conejitos jajaja
Graaaaaaaaaaacias! Hoy tuve un lunes negro con Git :p
💪💪
Gran artículo 😄!
No se por qué, pero por alguna razón cada vez que se usaba un ejemplo de algún comando lo leía con la voz de Freddy jajaja
Muy bien explicado y guardado para consultas futuras.
😎😬
Muy bien explicado!!
Que buen artículo, gracias.
Excelente aporte. Gracias
De mis comandos favoritos el git stash, salva mucho trabajo.
Saludos
Muy pro, vendría bien un nuevo post con otros commandos o cosas que hayas aprendido.
Wow, queda super bien despues de tomado el curso.Super buen resumen!
Yo actualmente estoy tomando el curso de Git y GitHub de Freddy, y sin duda es todo un dolor de cabeza, pero sé que va a ser para bien. Y tengo muchos compañeros desarrolladores que me dicen que Git sin duda despues de que lo haya aprendido y lo haya dominado me va a cambiar la vida por completo (aunque creo que ya lo hizo 😅👌)