¿Por qué git commits no contiene el nombre de la rama en la que se crearon?

20

Cuando trabajo con git en un equipo que usa ramas de características, a menudo me resulta difícil entender la estructura de la rama en la historia.

Ejemplo:

Digamos que hubo una función de rama de características / hacer café , y la corrección de errores continuó en el maestro en paralelo a la rama de características.

La historia podría verse así:

*     merge feature/make-coffee
|\
| *   small bugfix
| |
* |    fix bug #1234
| |
| *    add milk and sugar
| |
* |    improve comments
| |
* |    fix bug #9434
| |
| *    make coffe (without milk or sugar)
| |
* |    improve comments
|/
*

Problema

A primera vista, me resulta difícil saber de qué lado está la rama de características. Por lo general, necesito buscar varios comentarios en ambos lados para tener una idea de cuál es cuál. Esto se vuelve más complicado si hay varias ramas de características en paralelo (particularmente si son para características estrechamente relacionadas), o si se fusionó en ambas direcciones entre la rama de características y el maestro.

Como contraste, en Subversion, esto es significativamente más fácil porque el nombre de la sucursal es parte del historial, por lo que puedo decir de inmediato que una confirmación se realizó originalmente en "feature / make-coffee".

Git podría hacer esto más fácil al incluir el nombre de la rama actual en los metadatos de confirmación al crear una confirmación (junto con el autor, la fecha, etc.). Sin embargo, git no hace esto.

¿Hay alguna razón fundamental por la que esto no se hace? ¿O es que nadie quería la función? Si es lo último, ¿hay otras formas de entender el propósito de las ramas históricas sin ver el nombre?

sleske
fuente
1
Pregunta relacionada: Encontrar de qué rama proviene un commit . Se trata de cómo encontrar esta información a pesar del hecho de que git no la registra explícitamente.
sleske
1
Nota al uno mismo: Es interesante que en el commit Mercurial no contendrá el nombre de la sucursal. Parece que los desarrolladores de Mercurial tomaron una decisión de diseño diferente a la que hicieron los desarrolladores de git. Ver, por ejemplo, felipec.wordpress.com/2012/05/26/… para más detalles.
sleske

Respuestas:

14

Probablemente porque los nombres de las ramas solo tienen sentido dentro de un repositorio. Si estoy en el make-coffeeequipo y envío todos mis cambios a través de un guardián de puerta, mi masterrama podría ser arrastrada a la make-coffee-guisucursal del guardián de puerta , que se fusionará con su make-coffee-backendsucursal, antes de volver a cambiar a una make-coffeesucursal que se fusiona con la mastersucursal central .

Incluso dentro de un repositorio, los nombres de las sucursales pueden cambiar y cambian a medida que evoluciona su flujo de trabajo. masterpodría cambiar más tarde para ser llamado development, por ejemplo.

Como aludió CodeGnome, git tiene una sólida filosofía de diseño de no incluir algo en los datos si no es necesario. Se espera que los usuarios usen opciones git logy git diffgeneren los datos a su gusto después del hecho. Recomiendo probar algunos hasta que encuentre un formato que funcione mejor para usted. git log --first-parent, por ejemplo, no mostrará las confirmaciones de una rama que se está fusionando.

Karl Bielefeldt
fuente
2
Sin embargo, debe tenerse en cuenta que el primer padre suele ser el incorrecto. Porque, por lo general, uno simplemente atrae al maestro a lo que sea que estén trabajando y lo empuja para que su trabajo sea el primer padre y el segundo.
Jan Hudec
1
@ JanHudec: En realidad, esto depende :-). Si la persona que hace el trabajo lo fusiona, entonces sí. Si la fusión se produce más tarde, tal vez después de una revisión, entonces es más probable que el revisor haya verificado el master y fusione la rama de características en master, la pruebe y la empuje.
sleske
5

Seguimiento de la historia de la sucursal con --no-ff

En Git, un commit tiene antepasados, pero una "rama" es realmente solo el jefe actual de alguna línea de desarrollo. En otras palabras, un commit es una instantánea del árbol de trabajo en algún momento y puede pertenecer a cualquier número de ramas a la vez. Esto es parte de lo que hace que la ramificación de Git sea tan liviana en comparación con otros DVCS.

Las confirmaciones de Git no llevan información de sucursal porque no son necesarias para el historial de Git. Sin embargo, las fusiones que no son de avance rápido ciertamente pueden llevar información adicional sobre qué rama se fusionó. Puede asegurarse de que su historial contenga esta información pasando siempre la --no-ffbandera a sus fusiones. git-merge (1) dice:

   --no-ff
       Create a merge commit even when the merge resolves as a
       fast-forward. This is the default behaviour when merging an
       annotated (and possibly signed) tag.

Esto generalmente creará un mensaje de fusión similar a Merge branch 'foo', por lo que normalmente puede encontrar información sobre "ramas ancestrales" con una línea similar a la siguiente:

$ git log --regexp-ignore-case --grep 'merge branch'
CodeGnome
fuente
Gracias, pero eso (en su mayoría) no responde a mi pregunta. No estoy preguntando qué otras formas hay para encontrar la rama original, sino por qué la información no se incluye como metadatos regulares: es más una pregunta de diseño.
sleske
1
@sleske Creo que mi respuesta fue receptiva: Git no lo rastrea porque el historial de Git no lo necesita; No creo que se vuelva más fundamental que eso. Tendría que mirar la fuente para obtener detalles específicos, pero el historial se calcula efectivamente hacia atrás desde la punta de la rama actual. Siempre puede comparar eso con otros DVCS que tratan las ramas como objetos de primera clase y ver si le gusta más esa filosofía de diseño. YMMV.
CodeGnome
1
Ese último debería ser git log --merges.
Dan
3

La respuesta más simple es que los nombres de las ramas son efímeros. ¿Y si tuviera que, por ejemplo, cambiar el nombre de una rama ( git branch -m <oldname> <newname>)? ¿Qué pasaría con todos los compromisos contra esa rama? ¿O qué pasa si dos personas tienen sucursales que tienen el mismo nombre en diferentes repositorios locales?

Lo único que tiene significado en git es la suma de verificación del commit en sí. Esta es la unidad básica de seguimiento.

La información está ahí, simplemente no la está solicitando, y no está exactamente allí.

Git almacena la suma de comprobación de la cabeza de cada rama en .git/refs/heads. Por ejemplo:

... /. git / refs / heads $ ls test 
-rw-rw-r-- 1 michealt michealt 41 sep 30 11:50 prueba
... /. git / refs / heads $ prueba de gato 
87111111111111111111111111111111111111d4

(sí, estoy golpeando mis sumas de comprobación de git, no es especial, pero son repositorios privados que estoy viendo en ese momento que no necesitan que se filtre accidentalmente).

Al mirar esto y rastrear cada commit que tiene esto como un padre, o el padre de los padres o el padre de los padres ... puede averiguar en qué ramas se encuentra un compromiso determinado. Es solo un gran gráfico para representar.

Almacenar donde específicamente el nombre de una rama (que puede cambiar como se mencionó anteriormente) es una confirmación en la confirmación en sí es una sobrecarga adicional en la confirmación que no lo ayuda. Almacene el padre (s) del commit y es bueno.

Puede ver las ramas ejecutando el comando git log con el indicador correspondiente.

git log --branches --source --pretty = oneline --graph
git log --all --source --pretty = en línea --graph

Hay muchas formas diferentes de elegir lo que desea para eso: consulte la documentación de registro de git , la -allsección y las pocas opciones que siguen.

* 87111111111111111111111111111111111111d4 prueba Combinar rama 'prueba1' en prueba
| \  
El | * 42111111111111111111111111111111111111e8 prueba1 actualización: agregar cosas
* | dd11111111111111111111111111111111111159 prueba Combinar la rama 'prueba3' en la prueba
| \ \  

Esa 'prueba', 'prueba1' y 'prueba2' son las ramas a las que se comprometieron.

Tenga en cuenta que la documentación de registro de git es enorme y es muy posible obtener casi cualquier cosa que desee.


fuente
3

¿Por qué los git commits no contienen el nombre de la rama en la que se crearon?

Solo una decisión de diseño. Si necesita esa información, puede agregar un enlace prepare-commit-msg o commit-msg para aplicarlo en un único repositorio.

Después del desastre de BitKeeper, Linus Torvalds desarrolló git para que coincida con el proceso de desarrollo del kernel de Linux. Allí, hasta que se acepte un parche, se revisa, edita, pule varias veces (todo a través del correo), se cierra la sesión (= editar una confirmación), se selecciona, deambula por las ramas del teniente, a menudo se puede rebajar, más ediciones, cereza -púas y así sucesivamente hasta que finalmente se fusionen río arriba por Linus.

En dicho flujo de trabajo, puede que no haya un origen específico de una confirmación, y a menudo se crea una confirmación en alguna rama de depuración temporal en algún repositorio aleatorio privado sin importancia con nombres de ramas divertidos, ya que se distribuye git.

Tal vez esa decisión de diseño solo ocurrió por coincidencia, pero coincide exactamente con el proceso de desarrollo del kernel de Linux.

villekulla
fuente
2

Porque en Git los compromisos como "hacer café" se consideran parte de ambas ramas cuando se fusiona la rama característica. Incluso hay un comando para verificar eso:

% git branch --contains @
  feature/make-coffee
  master

Además, las sucursales son baratas, locales y etéreas; se pueden agregar, eliminar, renombrar, enviar y eliminar fácilmente del servidor.

Git sigue el principio de que todo se puede hacer, por lo que puede eliminar fácilmente una rama de un servidor remoto y puede reescribir fácilmente el historial.

Digamos que estaba en la rama 'maestra', e hice dos confirmaciones: 'hacer café' y 'agregar leche y azúcar', luego decido usar una nueva rama para eso, así que restablezco 'maestra' a 'origen /Maestro'. No es un problema, toda la historia aún está limpia: 'preparar café' y 'agregar leche y azúcar' no son parte del maestro, solo tienen función / hacen café.

Mercurial es lo contrario; No quiere cambiar las cosas. Las ramas son permanentes, el historial de reescritura está mal visto, no se puede simplemente eliminar una rama del servidor.

En resumen: Git te permite hacer todo, Mercurial quiere facilitar las cosas (y hace que sea realmente difícil dejarte hacer lo que quieras).

FelipeC
fuente