28 puntos por GN⁺ 2023-11-24 | 1 comentarios | Compartir por WhatsApp
  • Mucha gente piensa que la forma en que funcionan las ramas de Git no es intuitiva.
  • Se explica la diferencia entre el modelo intuitivo general sobre las ramas de Git y cómo se representan realmente las ramas dentro de Git.
  • Se muestra que el modelo intuitivo y la forma en que Git realmente funciona están, de hecho, muy estrechamente relacionados.
  • Se discuten las limitaciones del modelo intuitivo y por qué puede causar problemas.

Modelo intuitivo de las ramas

  • Mucha gente piensa en las ramas como si fueran las ramas de un árbol de manzanas.
  • En Git, las ramas no tienen un concepto de "padre", y eso es distinto de pensar que surgieron como una bifurcación desde main.

En Git, una rama es todo el historial

  • En Git, una rama no es simplemente el commit donde se bifurcó, sino que incluye el historial completo de todos los commits anteriores.
  • A través de un repositorio de ejemplo, se muestra que tanto main como mybranch tienen 4 commits.

Las ramas se almacenan como IDs de commit

  • Dentro de Git, las ramas se almacenan como pequeños archivos de texto que contienen un ID de commit.
  • El commit más reciente de cada rama queda registrado en ese archivo.
  • Como no existe una relación padre-hijo entre ramas, Git no conoce la relación entre ellas.

La intuición de la gente normalmente no está tan equivocada

  • Decir que la intuición de la gente sobre Git está "equivocada" es, en cierto sentido, algo absurdo.
  • Incluso un modelo "incorrecto" puede ser útil en la práctica.

Rebase usa el concepto "intuitivo" de rama

  • Rebase vuelve a aplicar sobre main únicamente los commits de la rama "intuitiva".
  • El resultado de rebase coincide con el modelo intuitivo.

Merge también usa el concepto "intuitivo" de rama

  • Merge no copia commits, pero sí necesita un commit base compartido.
  • La merge base encuentra el commit donde se bifurcó la rama según el modelo intuitivo.

Los pull requests de GitHub también usan la idea intuitiva

  • Si en GitHub creas un pull request para fusionar mybranch en main, solo se muestran los commits de la rama intuitiva.

La intuición es buena, pero tiene límites

  • La definición intuitiva de rama encaja bien con el trabajo real en Git, pero Git no puede reconocer de forma distinta una rama bifurcada desde main.

El tronco y las ramas bifurcadas

  • La gente percibe main y mybranch de manera diferente, y eso influye en cómo usa Git.
  • Git no distingue si una rama es una "bifurcación" de otra.

Git puede hacer rebase "al revés"

  • Como Git no sabe si una rama es una "bifurcación" de otra, el usuario debe saber cuándo y sobre qué rama hacer rebase.
  • Tanto git rebase main como el rebase inverso git rebase mybranch son posibles. Lo mismo ocurre con merge.

La ausencia de una jerarquía entre ramas en Git es algo extraña

  • Decir que la rama main no es especial surge de que Git no reconoce las relaciones entre ramas.
  • Sí existe una relación entre las ramas, pero Git no sabe nada al respecto.

La UI de ramas de Git también es extraña

  • Cuando quieres ver solo los commits "bifurcados", la forma de usar git log y git diff es distinta.

En GitHub, la rama predeterminada sí es especial

  • GitHub tiene una "rama predeterminada", y esta cumple un papel especial.

Opinión de GN⁺

Lo más importante de este texto es entender la diferencia entre la comprensión intuitiva que tiene la gente sobre las ramas de Git y la forma en que Git realmente funciona. Este artículo ayudará a ingenieros de software principiantes a entender mejor el concepto de ramas en Git y a usarlas de manera más efectiva. Ver cómo el modelo intuitivo de las ramas coincide con el trabajo real y cómo Git no maneja las relaciones entre ramas resulta interesante y útil.

1 comentarios

 
GN⁺ 2023-11-24
Comentarios en Hacker News
  • Una rama es un puntero a un commit, y ese puntero se actualiza cada vez que se crea un nuevo commit. Puede verse como un nombre flotante, similar a una etiqueta. Como los propios commits apuntan a sus commits padre, una rama termina siendo una cadena de commits relacionados con un punto de entrada con nombre. Si eliminas la rama, deja de existir esa etiqueta con nombre y solo queda la cadena de commits relacionados.
  • Es más fácil de entender si piensas en el linaje de los commits como punteros que van "hacia atrás" y no "hacia adelante". Una rama es un ID de commit, así que si sigues los enlaces a los padres puedes encontrar toda la historia de esa rama. El "punto de rama" es donde se encuentran dos cadenas de commits, y un commit de merge es especial porque indica que dos historias se unieron en una sola.
  • En proyectos personales, a mis amigos suele molestarles ver que manipulo cambios y punteros de ramas usando git reset --hard y git stash. Para deshacer un merge incorrecto, uso git reset --hard <último commit antes del merge>, y para aplicar pequeños cambios de una rama local a la rama principal, uso git stash, luego hago checkout a la rama principal y ejecuto git stash apply.
  • En Git no existe el concepto de que "main es especial", pero herramientas como GitLab ofrecen ramas protegidas para reducir errores. La idea de ramas "padre" e "hija" en realidad podría ser interesante, y haría falta admitir varias ramas "padre" para las ramas de soporte a largo plazo.
  • Al hacer merge, rebase o pull requests, hay que indicar explícitamente la otra rama. Git no sabe cuál es la rama base que el usuario tiene en mente. A veces puede que quieras hacer merge de una rama de funcionalidad en otra rama de funcionalidad, así que hay que especificar claramente qué rama se fusiona en cuál.
  • Aunque la intuición que tiene la gente pueda ser técnicamente parcialmente incorrecta, hay razones válidas por las que llegan a esa intuición.
  • Hay un tutorial interactivo dirigido a personas que ya saben usar git add y git commit. Este tutorial visualiza las ramas y ayuda a leerlas.
  • Si recuerdas que al ejecutar comandos de Git "siempre" modificas la rama actual, es "fácil" entender la sintaxis de Git. Por ejemplo, git merge my-branch fusiona my-branch en la rama actual, y git rebase my-branch hace rebase de la rama actual sobre my-branch.
  • Sería bueno que una rama (head) tuviera una "cola" que apuntara al commit base donde comenzó esa rama. Como las ramas se rebasan con frecuencia, a veces hay que pensar de dónde partieron. Sería más práctico si Git indicara que el commit base pertenece a main.
  • Al enviar "patches" a una lista de correo, opcionalmente puedes incluir el commit base. Esto se debe a que puede no estar claro si los cambios se basan en la versión más reciente, en la rama principal de desarrollo o en una rama de integración. También hay que tener presente la base al usar git range-diff. Esta herramienta compara dos rangos, como main..previous y main..current.
  • Al releer una opinión personal sobre las ramas, volví a aprender algunas cosas que había olvidado.