Git es sistema de control de versiones distribuido creado por Linus Torvalds en 2005 para el desarrollo del kernel Linux. De esa fecha para adelante Git ha evolucionado convirtiéndose en el sistema de control de versiones por excelencia para el desarrollo de proyectos Open-Source y de cara a los últimos años su popularidad ha alcanzado los desarrollos empresariales y proyectos privados junto al gran número de alternativas que podemos usar tener un repositorio en la nube.

Al haberse vuelto ubicuo en la industria de Software, muchos desarrolladores que están acostumbrados a utilizar un sistema de control de versiones centralizado como TFVC se están moviendo hacia Git, por eso hoy quiero contarles algunas cosas que nunca deben hacer con Git.

1. NUNCA hacer commit directamente a 'master'

Esto sobre todo aplica cuando trabajas colaborativamente usando un repositorio central principal pero también es válido si uno trabaja solo. Debemos evitar la mala costumbre de trabajar directamente sobre la rama master, no debemos trabajar nuestros cambios directamente pues estaremos perdiendo una de las características que más aprecio de Git, que es la capacidad de crear ramas instantáneamente (pues son solo la referencia a un commit).

El no usar una rama aislada para trabajar en nuevas características sobre nuestro código nos hará mucho más difícil y peligroso el sincronizar nuestras ramas y no nos permite subir commits para sub-tareas de una feature más grande sin desestabilizar nuestra rama.

Mi practica es crear una nueva rama para cosa nueva en la que trabajaré. De esta manera master (o develop) siempre estará listo para cambios menores rápidos o hotfixes.

2. NUNCA hacer git push --force

Normalmente, cuando hacemos push a nuestro repositorio central los commits que hemos realizado son enviados y colocados "tal cual" encima del estado actual de nuestra rama en el repositorio central. Sin embargo, cuando nuestro repositorio local no está sincronizado con (es decir actualizado con) el repositorio central, este último no podrá hacer merge y nos devolverá un mensaje de error.

Lo correcto en estos casos es que sincronicemos nuestro repositorio local en primer lugar, haciendo pull de nuestros cambios (revisar el punto 4) y una vez sincronizado nuestro repositorio local y estando seguros de que todo funcione bien proceder a hacer nuestro push. La peor cosa que podríamos hacer en este escenario es el terrible git push --force. Un push force sobrescribe toda la estructura y secuencia de commits en el repositorio central, tirando a la basura los commits de las demás personas.

La configuración por defecto en git nos permite hacer esto, pero en la mayoría (por no decir todos) los casos no deberíamos poder hacerlo. ¿Y cómo puedo evitar que alguien haga un push --force? Depende del servicio que utilices como repo central, pero de ser el estándar podemos configurarlo con el siguiente comando:

git config --system receive.denyNonFastForwards true

3. NUNCA subir archivos binarios

Los archivos de texto son un punto clave aquí, pues los cambios en esta clase de archivos son fácilmente detectables, pero es casi imposible para datos binarios. Los datos acerca de cambios en archivos binarios hacen imposible leer los commits. Sin embargo, hay otra muy buena razón para dejar fuera de nuestro repositorio y es que, generalmente estos archivos como imágenes, binarios compilados o incluso videos son mucho más grandes que los archivos de texto de nuestro código y si hacemos commit de ellos a nuestro repositorio, el tamaño de este se volverá muy grande.

Y aquí tenemos que aclarar que esto es muy importante y no porque el almacenamiento sea un problema, sino que el punto de tener un VCS distribuido es que sea rápido clonar y navegar entre las ramas. Siempre querremos coger una nueva máquina y clonar el repositorio tan rápido como sea posible. Querremos ser capaces de cambiar de ramas tan rápido como sea posible, pero si haces commit de un numero significativo de archivos binarios, pronto te darás cuenta de lo lento que estas tareas se vuelven.

4. NUNCA usar git pull

Ok esto podría sonar un tanto jalado de los pelos, pero es que si hacemos git pull no tendremos oportunidad de ver qué clase de cambios estamos tratando de incorporar (haciendo pull), podrían estos ser grandes refactorizaciones con un alto impacto o cambios sencillos de los que no hay que preocuparnos. La recomendación es usar git fetch periódicamente para actualizar solo la referencia a las ramas remotas, darle una inspección rápida y decidir qué hacer.

Para mantener el repositorio limpio, tus commits deberían estar encima de los cambios que los demás han subido al repositorio central hasta que tu hagas push, para esto en lugar de git pull deberemos usar git pull --rebase. Así los conflictos serán resueltos commit por commit y no todo en uno.

NOTA: No estamos recomendando hacer rebase de un repositorio remoto, hacer rebase de la historia local está bien, de hecho, es necesario para mantener limpia la historia, pero cambiar la historia de los commits de otras personas es una mala práctica.

5. NUNCA Usar Fast Forward

Si nosotros hacemos git merge directamente, no estamos asegurándonos de crear un merge commit. Los merge commit solo son necesarios cuando ambas ramas que queremos mezclar (merge) tienen nuevos commits.

¿Y porque son importantes los merge commits? Pues bien, sin un merge commit yo no puedo saber que commits representan una "feature" sobre la que se ha trabajado sin inspeccionar uno por uno los commits y sus mensajes.

Veámoslo en acción para no olvidarlo.

Con los siguientes comandos tendremos nuestro repositorio con nuestro primer commit:

git init test
cd test
echo "content" > file.txt
git add *
git commit -m "init"

Creamos una rama, modificamos el archivo file.txt y hacemos commit (hacemos 2 commits para que se vea mejor en la historia):

git checkout -b branch1
echo "modification" >> file.txt
git commit -am "branch1 modification 1"
echo "modification" >> file.txt
git commit -am "branch1 modif"

Y ahora hacemos merge sin la opción de fast forward.

git checkout master
git merge --no-ff --log --no-edit branch1

Ahora, otra vez creamos una rama, modificamos los archivos y hacemos commit.

git checkout -b branch2
echo "modif" >> file.txt
git commit -am "branch2 modif"
echo "modif" >> file.txt
git commit -am "branch2 modif"

Y finalmente, hacemos merge usando fast-forward:

git checkout master
git merge --ff-only branch2

Como pueden observar, no tenemos forma alguna de determinar en base a la historia la existencia de una rama y saber que commits fueron parte de esa feature. Así que no olviden usar merge con la opción de --no-ff.