En git, cómo hacer versiones para una docena de bibliotecas, todas trabajaron en paralelo

11

Estamos haciendo proyectos, pero reutilizamos mucho código entre los proyectos y tenemos muchas bibliotecas que contienen nuestro código común. A medida que implementamos nuevos proyectos, encontramos más formas de factorizar código común y ponerlo en bibliotecas. Las bibliotecas dependen unas de otras, y los proyectos dependen de las bibliotecas. Cada proyecto, y todas las bibliotecas utilizadas en ese proyecto, deben usar la misma versión de todas las bibliotecas a las que se refieren. Si lanzamos una pieza de software tendremos que corregir errores y tal vez agregar nuevas funciones durante muchos años, a veces durante décadas. Tenemos alrededor de una docena de bibliotecas, los cambios a menudo abarcan más de dos, y varios equipos trabajan en varios proyectos en paralelo, realizando cambios concurrentes en todas estas bibliotecas.

Recientemente cambiamos a git y configuramos repositorios para cada biblioteca y cada proyecto. Usamos stash como un repositorio común, hacemos cosas nuevas en las ramas de características, luego hacemos solicitudes de extracción y las fusionamos solo después de la revisión.

Muchos de los problemas con los que tenemos que lidiar en los proyectos requieren que hagamos cambios en varias bibliotecas y el código específico del proyecto. Estos a menudo incluyen cambios en las interfaces de la biblioteca, algunos de los cuales son incompatibles. (Si cree que esto suena sospechoso: interactuamos con el hardware y ocultamos hardware específico detrás de las interfaces genéricas. Casi cada vez que integramos el hardware de otro proveedor nos encontramos con casos en los que nuestras interfaces actuales no se anticipaban, por lo que tenemos que refinarlos). ejemplo, imagine un proyecto P1usando las bibliotecas L1, L2y L3. L1también usa L2y L3, y L2usa L3también. El gráfico de dependencia se ve así:

   <-------L1<--+
P1 <----+  ^    |
   <-+  |  |    |
     |  +--L2   |
     |     ^    |
     |     |    |
     +-----L3---+

Ahora imagine que una característica para este proyecto requiere cambios P1y L3que cambia la interfaz de L3. Ahora agregue proyectos P2y P3en la mezcla, que también se refieren a estas bibliotecas. No podemos permitirnos cambiarlos a la nueva interfaz, ejecutar todas las pruebas e implementar el nuevo software. Entonces, ¿cuál es la alternativa?

  1. implementar la nueva interfaz en L3
  2. hacer una solicitud de extracción L3y esperar la revisión
  3. fusionar el cambio
  4. crear una nueva versión de L3
  5. comience a trabajar en la función P1haciendo referencia a L3la nueva versión, luego implemente la función en P1la rama de funciones
  6. hacer una solicitud de extracción, revisarla y fusionarla

(Me he dado cuenta de que se me olvidó para cambiar L1y L2para la nueva versión. Y yo no sé ni dónde pegar esto en, ya que tendría que hacerse en paralelo con P1...)

Este es un proceso tedioso, propenso a errores y muy largo para implementar esta función, requiere revisiones independientes (lo que hace que sea mucho más difícil de revisar), no se escala en absoluto y es probable que nos saque del negocio porque se estancan tanto en el proceso que nunca hacemos nada.

Pero, ¿cómo empleamos la ramificación y el etiquetado para crear un proceso que nos permita implementar nuevas funciones en nuevos proyectos sin demasiados gastos generales?

sbi
fuente
1
Cambiar sus herramientas no debería afectar demasiado los procesos que tiene implementados. Entonces, ¿cómo lidiaste con este problema antes de cambiar a git?
Bart van Ingen Schenau
¿Es posible simplemente agregar un nuevo método a la interfaz sin romper el existente cuando demasiadas bibliotecas dependen de él? Normalmente, esa no es la mejor idea, pero al menos le permitiría "seguir adelante" implementando la nueva función y puede desaprobar adecuadamente el método anterior siempre que haya un momento libre. ¿O estas interfaces tienen demasiado estado para que funcionen "interfaces paralelas" como esa?
Ixrec
1
@ Ixrec: Me dijeron que "esta no es la manera más tonta" de hacer las cosas. Todos usan repositorios individuales para proyectos individuales, por lo que se decidió que nosotros también lo hagamos.
sbi
2
Yo diría que no son proyectos separados si frecuentemente tienen que cambiarse en conjunto. Los límites entre "proyectos" siempre deben tener algún tipo de garantía de compatibilidad con versiones anteriores a largo plazo.
Ixrec
1
@Ixrec: Integrar más hardware es similar a portar código a más plataformas: cuanto más lo haya hecho, menos tendrá que cambiar para otro hardware / plataforma. Entonces, a la larga, el código se estabilizará. Sin embargo, en este momento necesitaremos encontrar un proceso que nos permita permanecer en el mercado el tiempo suficiente para llegar allí.
sbi

Respuestas:

5

Es un poco poner lo obvio aquí, pero quizás valga la pena mencionarlo.

Por lo general, los repositorios de git se adaptan por lib / proyecto porque tienden a ser independientes. Actualizas tu proyecto y no te importa el resto. Otros proyectos que dependen de él simplemente actualizarán su lib cuando lo consideren conveniente.

Sin embargo, su caso parece altamente dependiente de componentes correlacionados, por lo que una característica generalmente afecta a muchos de ellos. Y todo tiene que ser empaquetado como un paquete. Dado que la implementación de una función / cambio / error a menudo requiere adaptar muchas bibliotecas / proyectos diferentes a la vez, quizás tenga sentido ponerlos a todos en el mismo repositorio.

Hay fuertes ventajas / inconvenientes en esto.

Ventajas:

  • Trazabilidad: la rama muestra todo lo cambiado en cada proyecto / lib relacionado con esta característica / error.
  • Agrupación: solo elija una etiqueta y obtendrá todas las fuentes correctas.

Inconvenientes:

  • Fusionar: ... a veces ya es difícil con un solo proyecto. Con diferentes equipos trabajando en sucursales compartidas, prepárese para prepararse para el impacto.
  • Factor de "Uy" peligroso: si un empleado estropea el repositorio al cometer algún error, podría afectar a todos los proyectos y equipos.

Depende de usted saber si el precio vale la pena.

EDITAR:

Funcionaría así:

  • La característica X debe implementarse
  • Crear sucursal feature_x
  • Todos los desarrolladores involucrados trabajan en esta rama y trabajan paralelamente en ella, probablemente en directorios dedicados relacionados con su proyecto / lib
  • Una vez que termine, revíselo, pruébelo, empaquételo, lo que sea
  • Fusionarlo de nuevo en el maestro ... y esta puede ser la parte difícil ya que mientras tanto feature_yy feature_zpuede haber sido agregado también. Se convierte en una fusión "entre equipos". Por eso es un serio inconveniente.

solo para que conste: creo que esto es en la mayoría de los casos una mala idea y debe hacerse con cautela porque el inconveniente de la fusión suele ser mayor que el que se obtiene a través de la gestión de dependencias / seguimiento de funciones adecuado.

dagnelies
fuente
Gracias, de hecho estamos viendo esto. Lo que no entiendo a la luz de esto es cómo haríamos la ramificación con git. En SVN, ramificar significa copiar un subárbol (en el repositorio) en otro lugar (en el repositorio). Con esto, es fácil crear ramas de cualquier subárbol en su repositorio, por lo que si tiene muchos proyectos allí, puede ramificar cada uno de ellos individualmente. ¿Hay algo como esto en git, o solo podríamos ramificar un repositorio completo?
sbi
@sbi: ramificaría todo el repositorio. No puede trabajar con subárboles en diferentes ramas, lo que podría derrotar el punto en su caso. Sin embargo, Git no "copiará" nada, simplemente rastreará los cambios en la rama en la que está trabajando.
Dagnelies
Por lo tanto, esto requiere que alguien cree una rama de características para que una biblioteca también combine todas las demás al fusionar o volver a crear. Este es un verdadero inconveniente. (Por cierto, SVN también solo hace copias perezosas).
sbi
@sbi: ver edición
dagnelies
1
Bueno, actualmente la mayoría de nosotros no estamos cómodos. :-/Además, incluso aquellos que están (y que presionaron para pasar a git), no saben cómo hacer que nuestro proceso de desarrollo se ajuste a git. Suspiro. Serán unos meses difíciles, me temo, hasta que las cosas empiecen a ponerse más tranquilas. Gracias de todos modos, la suya es la respuesta más / única útil hasta ahora.
sbi
4

La solución que está buscando es una herramienta de gestión de dependencias en coordinación con submódulos git

Herramientas como:

  • Maven
  • Hormiga
  • Compositor

Puede usar esas herramientas para definir las dependencias de un proyecto.

Puede requerir que un submódulo sea al menos versión > 2.xx o denotar un rango de versiones que sean compatibles = 2.2. * O menos que una versión particular <2.2.3

Cada vez que lanza una nueva versión de uno de los paquetes, puede etiquetarlo con el número de versión, de esa manera puede incorporar esa versión específica del código en todos los demás proyectos

Patricio
fuente
Pero la gestión de dependencias no es nuestro problema, esto está resuelto. Actualmente realizamos cambios regularmente en muchas bibliotecas y necesitamos minimizar la sobrecarga de crear nuevas versiones mientras mantenemos versiones estables del proyecto. Su respuesta no parece proporcionar una solución a esto.
sbi
@sbi Esto gestionaría la sobrecarga de crear nuevas versiones y mantener versiones estables del proyecto. Como puede dictar que el proyecto x se basa en el proyecto y versión 2.1.1, puede crear nuevas versiones del proyecto y que no afectarían al proyecto x.
Patrick
Nuevamente, declarar dependencias no es nuestro problema. Ya podemos hacer esto. El problema es cómo gestionar los cambios que atraviesan varios proyectos / bibliotecas de manera eficiente. Su respuesta no explica esto.
sbi
@sbi: ¿cuál es exactamente tu problema? Realiza los cambios, modifica la versión, actualiza las dependencias donde sea necesario y listo. Lo que describiste en tu publicación inicial es típico de maven & co. cosas. Cada distribución se basa en bibliotecas versionadas claramente definidas. ¿Cómo podría ser más claro?
Dagnelies
@arnaud: Los tiempos de respuesta para un proceso de este tipo para cambios (actualmente bastante comunes) que atraviesan tres o más capas nos matarían. Pensé que mi pregunta lo describía.
sbi
0

Submódulos

Debería intentar git submódulos , como se sugiere en un comentario.

Cuando el proyecto P1se refiere a los tres submódulos L1, L2y L3, en realidad se almacena una referencia al compromete particulares en los tres repositorios: esos son los que trabajan versiones de cada uno de bibliotecas para ese proyecto .

Por lo tanto, varios proyectos pueden funcionar con múltiples submódulos: P1puede referirse a la versión anterior de la biblioteca L1mientras el proyecto P2usaba la nueva versión.

¿Qué sucede cuando entrega una nueva versión de L3?

  • implementar una nueva interfaz en L3
  • confirmar, probar , hacer una solicitud de extracción, revisar, fusionar, ... (no puede evitar esto)
  • asegurar L2trabajos con L3, commit, ...
  • asegurar L1trabajos con nuevos L2, ...
  • asegúrese de que P1funcione con las nuevas versiones de todas las bibliotecas:
    • dentro P1's copia de trabajo local del L1, L2y L3, fetche los cambios que está interesado.
    • confirmar cambios, git add L1 L2 L3para confirmar la nueva referencia a los módulos
    • solicitud de extracción P1, prueba, revisión, solicitud de extracción, fusión ...

Metodología

Este es un proceso tedioso, propenso a errores y muy largo para implementar esta función, requiere revisiones independientes (lo que hace que sea mucho más difícil de revisar), no se escala en absoluto y es probable que nos saque del negocio porque se estancan tanto en el proceso que nunca hacemos nada.

Sí, requiere revisiones independientes, porque cambia:

  • la biblioteca
  • bibliotecas que dependen de ello
  • proyectos que dependen de múltiples bibliotecas

¿Estaría fuera del negocio porque entrega basura? (Tal vez no, en realidad). En caso afirmativo, debe realizar pruebas y revisar los cambios.

Con las herramientas git apropiadas (incluso gitk), puede ver fácilmente qué versiones de las bibliotecas usa cada proyecto, y puede actualizarlas de forma independiente de acuerdo con sus necesidades. Los submódulos son perfectos para su situación y no retrasarán su proceso.

Tal vez pueda encontrar una manera de automatizar parte de este proceso, pero la mayoría de los pasos anteriores requieren cerebros humanos. La forma más efectiva de reducir el tiempo sería garantizar que sus bibliotecas y proyectos sean fáciles de evolucionar. Si su base de código puede manejar nuevos requisitos con gracia, las revisiones de código serán más simples y tomarán poco de su tiempo.

(Editar) otra cosa que podría ayudarlo es agrupar las revisiones de código relacionadas. Confirma todos los cambios y espera hasta que propague esos cambios a todas las bibliotecas y proyectos que los utilizan antes de sumbir las solicitudes de extracción (o antes de que se encargue de ellas). Terminas haciendo una revisión más grande para toda la cadena de dependencia. Tal vez esto pueda ayudarlo a ahorrar tiempo si cada cambio local es pequeño.

volcado de memoria
fuente
Describe cómo resolver el problema de dependencia (que, como he dicho antes, hemos resuelto) y niega el problema que estamos teniendo. ¿Por qué te molestas? (Fwiw, se escribe el software que las plantas transmisiones de potencia limpia, segura, y el código revisado a fondo es una característica primordial..)
SBI
@sbi ¿Qué son los submódulos si no es un caso especial de ramificación y etiquetado? Usted piensa submódulos están a punto de gestión de la dependencia, porque también hacer un seguimiento de las dependencias. Pero claro, reinventa los submódulos con etiquetas si quieres, no me importa. No entiendo su problema: si el código revisado es una característica principal, debe presupuestar un tiempo para las revisiones. No estás empantanado, vas lo más rápido posible con las restricciones impuestas.
coredump
Los comentarios son muy importantes para nosotros. Esa es una de las razones por las que nos preocupa que un problema (y sus revisiones) se divida en varias revisiones para varios cambios en varios repositorios. Además, no queremos atascarnos en administrarnos hasta la muerte por un problema, es que preferimos pasar el tiempo escribiendo y revisando el código. Re submódulos: hasta ahora, lo único que he escuchado acerca de ellos es "no te molestes, esta no es la manera git". Bueno, dado que nuestros requisitos parecen ser tan únicos, tal vez deberíamos volver a examinar esta decisión ...
sbi
0

Entonces, lo que entiendo es que para P1 desea cambiar la interfaz L3 pero desea que los otros P2 y P3 que dependen de la interfaz L3 cambien de inmediato. Este es un caso típico de compatibilidad con versiones anteriores. Hay un buen artículo sobre esta preservación de la compatibilidad con versiones anteriores

Hay varias formas de resolver esto:

  • Debe crear nuevas interfaces cada vez que puedan ampliar las interfaces antiguas.

O

  • Si desea retirar la interfaz anterior después de un tiempo, puede tener varias versiones de interfaces y una vez que todos los proyectos dependientes se muevan, elimine las interfaces más antiguas.
Uday Shankar
fuente
1
No, la compatibilidad con versiones anteriores se garantiza a través de las ramas de lanzamiento y no es nuestro problema. El problema es que nos sentamos en una base de código que cambia rápidamente, que ahora queremos separar en bibliotecas, a pesar de que las interfaces todavía están en la fase en que cambian a menudo. Sé cómo manejar tales bestias en SVN, pero no sé cómo hacerlo en git sin ahogarme en la administración.
sbi
0

Si estoy resolviendo su problema correctamente:

  • Tiene 4 módulos interrelacionados, P1 y L1 a L3
  • necesita hacer un cambio a P1 que finalmente afectará a L1 a L3
  • cuenta como una falla del proceso si tiene que cambiar los 4 juntos
  • cuenta como una falla del proceso si tiene que cambiarlos todos de 1 en 1.
  • cuenta como una falla del proceso si tiene que identificar de antemano los fragmentos en los que se deben realizar los cambios.

Entonces, el objetivo es que puedes hacer P1 y L1 de una vez, y luego un mes después hacer L2 y L3 en otra.

En el mundo de Java, esto es trivial, y quizás la forma predeterminada de trabajar:

  • todo va en un repositorio sin uso relevante de ramificación
  • Los módulos se compilan + vinculan entre sí por Maven en función de los números de versión, no por el hecho de que todos están en el mismo árbol de directorios.

Por lo tanto, puede tener el código en su disco local para L3 que no se compilaría si se compilara contra la copia del P1 en el otro directorio de su disco; Afortunadamente no lo está haciendo. Java puede hacer esto directamente porque compilar / vincular cuentos contra archivos jar compilados, no sobre el código fuente.

No conozco una solución preexistente de uso generalizado para este problema para el mundo C / C ++, y me imagino que casi no quieres cambiar de idioma. Pero algo podría piratearse fácilmente con archivos de creación que hicieran lo mismo:

  • bibliotecas instaladas + encabezados a directorios conocidos con números de versión incrustados
  • Se cambiaron las rutas del compilador por módulo al directorio para los números de versión apropiados

Incluso podría usar el soporte C / C ++ en maven , aunque la mayoría de los desarrolladores de C lo mirarían de manera extraña si lo hiciera ...

soru
fuente
"cuenta como una falla del proceso si tienes que cambiar los 4 juntos" . En realidad no lo haría. De hecho, esto es exactamente lo que hicimos usando SVN.
sbi
En ese caso, supongo que no hay problema con simplemente poner todos los proyectos en el repositorio.
soru
Ahora estamos evaluando poner las bibliotecas en solo dos repositorios. Eso sigue siendo más de uno, pero mucho menos que "uno para cada proyecto", y las bibliotecas se pueden dividir en dos grupos. ¡Gracias por tu contribución!
sbi
PD: "Me imagino que apenas quieres cambiar de idioma". Esto es material incrustado. :)
sbi
-1

Hay una solución simple: cortar ramas de lanzamiento en todo el repositorio, fusionar todas las correcciones a todos los lanzamientos enviados activamente (es fácil en caso claro debería ser posible en git).

Todas las alternativas crearán un desastre horrible con el tiempo y con el crecimiento del proyecto.

zzz777
fuente
¿Podría por favor elaborar? No estoy seguro de lo que estás sugiriendo.
sbi
En caso claro, define un punto de ramificación como rama base y marca de tiempo. Tiene la siguiente jerarquía: rama base -> versión-desarrollo-rama -> rama-desarrollo privado. Todo el desarrollo se realiza en sucursales privadas y luego se fusiona en la jerarquía. Las ramas de lanzamiento del cliente se dividen release-development-branch. No estoy tan familiarizado con git, pero parece lo más parecido a aclarar casos entre los sistemas de control de fuente libre.
zzz777
Una lectura cuidadosa de mi pregunta debería haberle mostrado que tenemos problemas con la sobrecarga involucrada en la propagación de cambios entre repositorios. Esto no tiene nada que ver con tu respuesta.
sbi
@sbi Lamento haber entendido mal su pregunta. Y me temo que tarde o temprano te enfrentarás a un horrible desastre.
zzz777