2010-04-12 21 views
342

Actualmente, nuestra compañía está utilizando un modelo simple de troncalización/liberación/revisión de revisiones y desearía asesoramiento sobre qué modelos de ramificación funcionan mejor para su empresa o proceso de desarrollo.¿Qué modelos de ramificación de Git trabajan para usted?

  1. flujos de trabajo/modelos de ramificación

    A continuación se presentan los tres principales descripciones de este he visto, pero se contradicen parcialmente entre sí o que no van lo suficientemente lejos de resolver los problemas posteriores que Me he topado con (como se describe a continuación). Por lo tanto, hasta ahora nuestro equipo no tiene soluciones excelentes. ¿Estás haciendo algo mejor?

  2. Fusión vs cambio de base (enredado vs historia secuencial)

    caso de que uno pull --rebase o espere con la fusión de nuevo a la línea principal hasta que su la tarea ha terminado? Personalmente me inclino por la fusión, ya que conserva una ilustración visual de la base en la que se inició y finalizó una tarea, e incluso prefiero merge --no-ff para este propósito. Sin embargo, tiene otros inconvenientes. Además, muchos no se han dado cuenta de la propiedad útil de la fusión, que no es commutative (fusionar una rama de tema en maestro no significa fusionar el maestro en la rama de tema).

  3. Busco a un flujo de trabajo natural de

    A veces se cometen errores porque nuestros procedimientos no captan una situación específica con reglas simples. Por ejemplo, una solución necesaria para las versiones anteriores debería basarse, por supuesto, en una etapa posterior para que sea posible fusionarse en todas las ramas necesarias (¿el uso de estos términos es lo suficientemente claro?). Sin embargo, sucede que una solución llega al maestro antes de que el desarrollador se dé cuenta de que debería haberse colocado más adelante, y si eso ya está presionado (incluso peor, fusionado o algo basado en él), la opción restante es elegir, con sus peligros asociados ¿Qué reglas simples como esas usas? También en esto se incluye la incomodidad de una rama de tema que necesariamente excluye otras ramas de tema (suponiendo que estén ramificadas a partir de una línea base común). Los desarrolladores no quieren terminar una función para empezar otra sensación de que el código que acaba de escribir ya no está allí

  4. Cómo evitar la creación de conflictos de fusión (debido a cereza-escoge)?

    Lo que parece una forma segura de crear un conflicto de combinación es elegir entre las ramas, nunca se pueden fusionar de nuevo? Sería posible que al aplicar el mismo compromiso en revertir (¿cómo hacer esto?) En cualquiera de las ramas se resuelva esta situación? Esta es una de las razones por las que no me atrevo a impulsar un flujo de trabajo en gran medida basado en fusiones.

  5. ¿Cómo descomponerse en ramas tópicas?

    Nos damos cuenta de que sería increíble ensamblar una integración completa de las ramas temáticas, pero a menudo el trabajo de nuestros desarrolladores no está claramente definido (a veces tan simple como "hurgar") y si algún código ya se ha tema "misc", no se puede sacar de allí de nuevo, de acuerdo con la pregunta anterior? ¿Cómo trabajas definiendo/aprobando/graduando/liberando tus ramas temáticas?

  6. Los procedimientos correctos, como la revisión del código y la graduación serían, por supuesto, encantadores.

    Pero simplemente no podemos mantener las cosas lo suficientemente desenredadas para gestionar esto, ¿alguna sugerencia? ramas de integración, ilustraciones?

A continuación se presenta una lista de preguntas relacionadas (! Gracias por this PDF)

También puedes ver lo que escribe en Plastic SCM task driven development, y si El plástico no es su elección, el estudio nvie's branching model y su supporting scripts.

+1

Hah, gracias, de hecho lo ha hecho ... De hecho, he leído la mayor parte de eso ... cosas :-). Es algo por lo que soy conocido: no conformarme con la solución mediocre, sino continuar buscando la perfecta. A menudo esto es un error, pero en este caso hay mucho en juego y las soluciones a mano son demasiado desordenadas o deficientes como para seguir buscando. Entonces decidí enumerar todos los problemas que tengo con eso. –

+0

Plastic SCM blog arrojó su opinión en la discusión, es al menos perspicaz: http://codicesoftware.blogspot.com/2010/08/branch-per-task-workflow-explained.html –

+1

Tienes que tener cuidado al usar " merge --no-ff ", mira esto para algunas advertencias http://sandofsky.com/blog/git-workflow.html – Doppelganger

Respuesta

80

La característica más preocupante nuevos desarrolladores a DVCS tienen que darse cuenta es sobre el publication process:

  • puede importar (fetch/pull) lo reporto a distancia que necesita
  • se puede publicar (push) a cualquier (desnudo) reporto que quiere

partir de eso, se puede seguir una serie de reglas para hacer más fácil a sus preguntas:

  • sólo se rebase una rama si no se ha introducido (no empujado desde la última rebase)
  • solamente empujar a un acuerdo de recompra desnudo (obligatorio desde Git1.7)
  • siguen Linus's advices on rebase and merges

Ahora:

flujos de trabajo/modelos de ramificación:

cada flujo de trabajo está ahí para support a release management process, y que se adapta a cada proyecto.
Lo que puedo agregar al flujo de trabajo que mencionas es: cada desarrollador no debe crear una rama de características, solo una rama de "desarrollo actual", porque la verdad es que el desarrollador a menudo no sabe qué exactamente producirá su sucursal : una característica, varias (porque terminó siendo una característica demasiado compleja), ninguna (porque no estaba lista a tiempo para la versión), otra característica (porque la original tenía "morphed") ...

Solo una El "integrador" debería establecer ramas de características oficiales en un repositorio "central", que luego los desarrolladores pueden buscar para volver a fusionar/fusionar la parte de su trabajo que se ajusta a esa característica.

Fusión vs cambio de base (enredado vs historia secuencial):

me gusta mi respuesta usted menciona ("Workflow description for git usage for in-house development")

Busco a un flujo de trabajo natural de:

de correcciones, puede ayudar a asociar cada solución con un ticket de un seguimiento de errores, lo que ayuda al desarrollador a recordar dónde (es decir, en qué rama, es decir, una rama dedicada "para reparaciones") él/ella debe confirmar tales modificaciones.
Entonces, los ganchos pueden ayudar a proteger un repositorio central contra los empujes de correcciones de errores no validadas o de ramas de las cuales no se debe presionar. (no hay una solución específica aquí, todo esto debe adaptarse a su entorno)

¿Cómo evitar la creación de conflictos de fusión (debido a la selección selectiva)?

Según lo indicado por Jakub Narębski en his answer, la recolección de cerezas se debe reservar para las situaciones poco frecuentes donde se requiera.
Si su configuración implica una gran cantidad de recolección de cereza (es decir, "no es raro"), entonces algo está apagado.

¿Aplicaría el mismo compromiso en revertir (cómo hacerlo?)

git revert debe tener cuidado de que, pero eso no es lo ideal.

¿Cómo descomponerse en las ramas tópicas?

Mientras una rama que aún no ha sido empujado por todas partes, un desarrollador debe reorganizar su historia de confirmaciones (una vez que él/ella finalmente ver el desarrollo toma una forma más definitiva y estable) en:

  • varias ramas si es necesario (una por función clara identificado)
  • un conjunto coherente de confirmaciones dentro de una rama (ver Trimming Git Checkins)

¿Procedimientos adecuados, como la revisión del código y la graduación?

ramas de integración (integración en un dedicado) repo puede ayudar al desarrollador:

  • rebase su/su desarrollo en la parte superior de esa rama integración remoto (Tire --rebase)
  • resolver localmente
  • empujar el desarrollo a la cesión temporal
  • cheque con el integrador que no da lugar a un desastre;)
+0

Gracias VonC, consideraré su respuesta lo antes posible! –

+0

@UncleCJ: como puede ver, esta no es exactamente * la respuesta * final a su "última pregunta";) – VonC

+0

Entiendo, y también tengo un buen sentido de la ironía, está bien ;-) –

19

Creo, y podría estar equivocado, que una de las cosas que más se malinterpreta sobre git es su naturaleza distribuida. Esto hace que sea muy diferente decir subversión en la forma en que puede trabajar, aunque puede imitar el comportamiento de SVN si lo desea. El problema es prácticamente cualquier flujo de trabajo, lo que es genial pero también engañoso.

Si entiendo bien el desarrollo del kernel (me centraré en eso), todos tienen su propio repositorio de git para desarrollar el kernel. Hay un repositorio, linux-2.6.git, atendido por Torvalds, que actúa como repositorio de versiones. Las personas clonan desde aquí si desean comenzar a desarrollar una función contra la rama de "publicación".

Otros repositorios hacen algún desarrollo. La idea es clonar desde linux-2.6, ramificar tantas veces como desee hasta el punto en que tenga una función "nueva" que funcione. Luego, cuando esté listo, puede ponerlo a disposición de una persona que se considere confiable, que separará esta rama de su repositorio y la fusionará con la corriente principal. En el kernel de Linux esto ocurre en varios niveles (tenientes de confianza) hasta que llega a linux-2.6.git y en ese momento se convierte en "kernel".

Ahora aquí es donde se vuelve confuso. Los nombres de las sucursales no necesitan ser consistentes en todos los repositorios. Entonces puedo git pull origin master:vanilla-code y obtener una rama del maestro origin en una rama en mi repositorio llamado vanilla-code. Si sé lo que está pasando, realmente no importa: se distribuye en el sentido de que todos los repositorios son iguales entre sí y no solo compartidos en varias computadoras como SVN.

Así que, con todo esto en mente:

  1. creo que corresponde a cada programador cómo hacen su ramificación. Todo lo que necesita es un repositorio central para gestionar lanzamientos, etc. El tronco podría ser head. Las versiones pueden ser etiquetas o ramas y las revisiones son probablemente ramas en sí mismas. De hecho, probablemente haría lanzamientos como ramas para que pueda seguir parchándolos.
  2. Me fusionaría y no rebase. Si por ejemplo se toma un repositorio, clonarlo, rama y hacer algo de dev, a continuación, tire de su origin debe, en su repositorio, probablemente haría otra rama y fusionar la última master en yourbranch para que otra persona puede tirar de sus cambios con la mayor poco esfuerzo como sea posible. Rara vez existe la necesidad de una verdadera rebase, en mi experiencia.
  3. creo que es un caso de la comprensión de la forma en que funciona Git y lo que puede hacer. Toma un tiempo y mucha buena comunicación. Empecé a entender realmente lo que sucede cuando comencé a usar git con otros desarrolladores, e incluso ahora, algunas cosas de las que no estoy seguro.
  4. Los conflictos de combinación son útiles. Lo sé, lo sé, quieres que todo funcione, pero el hecho es que los cambios en el código y necesitas fusionar los resultados en algo que funcione. Los conflictos de fusión son, de hecho, solo más programación. Nunca he encontrado una explicación fácil para qué hacer con ellos, así que aquí está: Tenga en cuenta los archivos que tienen los conflictos de fusión, ir y cambiarlos a lo que deberían ser, git add . y luego git commit.
  5. Sin embargo, le conviene. Como he dicho, el repositorio git de cada usuario es el suyo y los nombres de las ramas no necesitan ser el mismo. Si tuviera un repositorio provisional, por ejemplo, podría aplicar un esquema de denominación, pero no es necesario para cada desarrollador, solo en el repositorio de publicación.
  6. Esta es la etapa de fusión. Solo se fusiona en ramas de publicación, etc., cuando considera que el código se revisará/pasará la prueba de calidad.

Yo espero que ayude. Me doy cuenta de que VonC acaba de publicar una explicación muy similar ... ¡No puedo escribir lo suficientemente rápido!

Editar algunas reflexiones adicionales sobre el uso de git en un entorno comercial, ya que esto parece relevante para el PO de los comentarios:

  • El repositorio de liberación, lo llamaremos product.git, es accesible por un número de programadores sénior/personal técnico responsable de cuidar el producto en sí. Son análogos al papel de los mantenedores en OSS.
  • Estos programadores probablemente también en el desarrollo de la parte principal de las nuevas versiones, por lo que también podría codificar a sí mismos y mantener repositorios Varios. Podrían administrar repositorios de etapas para características realmente nuevas y también podrían tener sus propios repositorios.
  • Debajo están los programadores responsables de desarrollar bits individuales. Por ejemplo, alguien podría ser responsable del trabajo de UI. Por lo tanto, administran el repositorio de UI.git.
  • Debajo de ellos son los programadores reales que desarrollan las características que todo su trabajo cotidiano.

Entonces, ¿qué sucede? Bueno, todos se toman al inicio de cada día desde la fuente "ascendente", es decir, el repositorio de versiones (que probablemente también contenga el último material del desarrollo del día anterior). Todos hacen esto, directamente.Esto irá en una rama en su repositorio, probablemente llamado "maestro" o tal vez si me llaman "último". El programador entonces hará un poco de trabajo. Este trabajo puede ser algo de lo que no están seguros, por lo que hacen una sucursal, hacen el trabajo. Si no funciona, pueden eliminar la rama y regresar. Si lo hace, deberán fusionarse en la rama principal en la que están trabajando actualmente. Diremos que se trata de un programador de interfaz de usuario que trabaja en latest-ui, por lo que hace git checkout latest-ui seguido de git merge abc-ui-mywhizzynewfeature. Él luego le dice a su líder técnico (el líder de interfaz de usuario) hey, he completado esa tarea, tire de mí. Por lo tanto, la ventaja de UI es git pull user-repo lastest-ui:lastest-ui-suchafeature-abc. La guía de UI luego lo mira en esa rama y dice, en realidad, eso está muy bien, lo fusionaré en ui-latest. Luego puede decirle a todos los que están debajo de él que lo extraigan de sus ramas ui-latest o del nombre que le hayan dado, por lo que los desarrolladores exploran la función. Si el equipo está contento, el líder de UI podría pedirle al líder de prueba que lo retire y fusionar los cambios. Esto se propaga a todos (después del cambio) que lo prueba y envía informes de fallos, etc. Finalmente, si la función pasa la prueba, etc., uno de los mejores clientes técnicos podría fusionarlo en la copia de trabajo actual del programa, en cuyo momento todos los cambios se propagan de nuevo hacia abajo. Y así.

No es una forma "tradicional" de trabajar y está diseñada para ser "dirigida por pares" en lugar de "jerárquica" como SVN/CVS. En esencia, todos tienen acceso de compromiso, pero solo localmente. Es el acceso al repositorio y el repositorio que designa como el repositorio de publicación que le permite usar la jerarquía.

+0

+1 para la aclaración de aspecto "distribuido". – VonC

+0

Muchas gracias por su amplia respuesta (y votos), la leeré un par de veces más para extraer información útil de ella. Sin embargo, somos una empresa, no un comité de desarrollo de OSS ;-), y tengo que ayudar a mis desarrolladores con directrices más claras que "juguetear como lo desee en su propio repositorio". ¡Veamos a dónde conduce esta publicación, siento un buen impulso, sigo viniendo! –

+0

@VonC Gracias. @UncleCJ es verdad, pero lo es, estoy seguro, tiene administradores de versiones, etc. Cualquier persona que tenga acceso al repositorio puede hacer estas cosas. En cuanto al desarrollo, ¿por qué no dar libertad a los desarrolladores, dentro de lo razonable, para que se ramifiquen? Siempre que tenga algún protocolo para acordar fusiones y su (s) depósito (s) central (es) se nombren como desee, no hay ningún problema. Habiendo dicho eso, un esquema de nomenclatura común no es una mala idea. Tiendo a usar initials-version-feature-subbranches para ramas personales y versiones para branches. –

8

Un modelo que he utilizado con buenos resultados es el siguiente:

A "bendito" repo todo el mundo empuja y tira a/de, básicamente, una topología de cliente-servidor.

No hay una rama principal, por lo que ningún desarrollador puede insertar ningún código en la "línea principal".

Todos los desarrollos se realizan en las ramas temáticas. Nos nombres de espacio de nombres para detectar fácilmente quién es el responsable de ello: jn/newFeature o jn/issue-1234

También hay una asignación de 1 a 1 entre ramas y tarjetas de kanban/scrum en la pizarra.

Para liberar una rama, se envía al repositorio bendecido y la tarjeta kanban se mueve para su revisión.

Luego, si la rama es aceptada por la revisión, es un candidato para un lanzamiento.

Una versión se produce cuando un conjunto de ramas aceptadas se fusionan y se etiquetan con un número de versión.

Al presionar la nueva etiqueta para el bendecido repositorio, existe una nueva base posible para las nuevas características.

Para evitar conflictos de fusión, se les pide amablemente a los desarrolladores que actualicen (fusionen) sus ramas inéditas a la última etiqueta de lanzamiento.

1

Personalmente, intento mantener solo el código listo para liberar en la rama principal.

Cuando trabajo en una nueva característica o corrección de errores lo hago en una sucursal. También realizo una prueba unitaria en la sucursal. Si todo sale bien, solo entonces fusiono/rebase nuevamente en el maestro.

que tratar de utilizar las convenciones de nombres de rama común, así, como por ejemplo:

  • corrección de errores/recursive_loop
  • corrección de errores/sql_timeout
  • función/new_layout
  • función
  • /enhanced_search
Cuestiones relacionadas