Salta el contingut
 

Estratègies de ramificació

Joan Puigcerver Ibáñez

Llicència: CC BY-NC-SA 4.0

(Reconeixement - NoComercial - CompartirIgual) 🅭

Estratègies de ramificació

Quan es treballa en un projecte, sobretot si hi participen moltes persones, és imprescindible adoptar una metodologia de treball que facilite la gestió i el desenvolupament del projecte.

Si, a més, s'utilitza Git com a sistema de control de versions, cal una estratègia de ramificació: un conjunt de regles i pautes que defineixen el flux de treball mitjançant branques. Els objectius d'una estratègia de ramificació són:

  • Claredat: proporciona un flux de treball clar i coherent per a gestionar els canvis del codi.
  • Desenvolupament paral·lel: permet treballar en diverses funcionalitats alhora.
  • Col·laboració: facilita la col·laboració entre les persones de l'equip.
  • Estabilitat: ajuda a mantindre un codi estable i preparat per a posar-lo en producció.
  • Ordre: manté una història del projecte ordenada i coherent.

A més, les estratègies de ramificació es poden combinar amb altres ferramentes, com ara les Pull Requests, que es presenten en el Bloc 6: Gestió de projectes.

No obstant això, utilitzar una estratègia de ramificació pot suposar una sobrecàrrega en projectes xicotets o amb poques persones. És important adaptar la metodologia a les necessitats del projecte i no seguir-la de manera estricta si no aporta valor afegit.

No cal utilitzar tots els tipus de branques.

Per exemple, en projectes xicotets, potser no cal una branca de desenvolupament develop ni branques de llançament release/*.

Branques amb un propòsit únic

Les estratègies de ramificació més habituals es basen a crear diferents tipologies de branques, cadascuna amb un propòsit concret i una sèrie de regles per a crear-les, incorporar-les i eliminar-les:

  • Branca principal (main): branca on es troba la versió estable del projecte.

  • Branca de desenvolupament (development, develop, dev): branca on es troba l'estat actual del projecte, on s'incorporen les funcionalitats acabades i provades.

    • En un primer moment, es crea a partir de la branca main.
    • S'utilitza per a integrar les branques de funcionalitat feature/*, de manera que va avançant respecte de la branca main.
    • Es fusiona amb la branca main quan es prepara una nova versió del projecte.
  • Branques de funcionalitat (feature/*, feat/*, fix/*...): per a cada funcionalitat nova es crea una branca independent, on es programa i es prova.

    • Es creen a partir de la branca develop.
    • Es fusionen amb la branca develop una vegada acabades.
    • Es poden eliminar després d'integrar-les.
    • El prefix de les branques es pot adaptar per a indicar el tipus de funcionalitat.
  • Branques de llançament (release/*): branques on es preparen els canvis per a publicar una nova versió del projecte.

    • Es creen a partir de la branca develop.
    • Es fusionen amb les branques develop i main una vegada acabades.
    • Es poden eliminar una vegada fusionades.
    • Normalment, es crea una etiqueta amb la versió publicada.
  • Branques de correcció (hotfix/*): branques per a corregir errors crítics en la versió publicada del projecte.

    • Es creen a partir de la branca main.
    • Es fusionen amb les branques develop i main una vegada acabades.

Branca principal i de desenvolupament

La branca principal és la branca on es troba la versió publicada i estable del projecte. Normalment s'anomena main.

La branca de desenvolupament és la branca on es troba l'estat actual del projecte, on s'incorporen les funcionalitats noves que ja estan implementades i provades, però que encara no s'han publicat. Normalment s'anomena dev, develop o development.

Branca principal i de desenvolupament Branca principal i de desenvolupament

Figura 1. Branca principal i de desenvolupament.

Branques de funcionalitat

Les branques de funcionalitat són les branques on cada persona de l'equip fa les seues contribucions, de manera paral·lela i independent de la resta.

Normalment, s'utilitza un prefix comú per a identificar aquestes branques. El més habitual és feature/, seguit del nom de la funcionalitat. No obstant això, el prefix pot variar, fins i tot per a indicar el tipus de funcionalitat o la naturalesa dels canvis: feat/, feature/, fix/, bugfix/, enhancement/...

Branques de funcionalitat Branques de funcionalitat

Figura 2. Branques de funcionalitat.

El flux de treball amb aquestes branques és el següent:

  1. Es creen a partir de la branca develop.

    En la Figura 2 s'observa que totes les branques feature/ s'han creat a partir de la branca develop, però no necessàriament en el mateix punt.

  2. S'integren en la branca develop una vegada s'han implementat i provat els canvis.

  3. Es poden eliminar després d'integrar-les.

Recomanacions i bones pràctiques

  • Utilitza noms descriptius i coherents, que indiquen clarament el propòsit i el contingut de les branques, i evita els noms genèrics o massa concrets.

  • Incorpora els canvis de develop de manera regular.

    És preferible mantindre les branques de funcionalitat actualitzades amb els canvis del projecte per a evitar resolucions de conflictes enormes en el moment d'integrar-les.

Integració

Per a integrar les funcionalitats en la branca de desenvolupament develop, se segueix aquest procés:

  1. Sincronitza l'estat del repositori local amb el remot.

    git fetch
    
  2. Actualitza la branca local develop amb els canvis del remot amb git pull.

    git checkout develop
    git pull --ff-only # (1)!
    
    1. Per a evitar possibles conflictes i errors, es recomana configurar git pull perquè només puga incorporar els canvis de manera directa (fast-forward).

      git config [--global] pull.ff only
      
  3. Actualitza la branca feature/* amb els canvis nous de develop. Aquest pas varia segons la tècnica triada per a la integració, que s'explica en els apartats dedicats a cada tècnica.

    git checkout feature/nom-funcionalitat
    git merge --no-ff develop
    

    No és necessari, però es recomana per a mantindre la branca de funcionalitat actualitzada.

    git checkout feature/nom-funcionalitat
    git merge --no-ff develop
    
    git checkout feature/nom-funcionalitat
    git rebase develop
    
    git checkout feature/nom-funcionalitat
    git rebase develop
    
  4. Incorpora els canvis de la branca feature/* en la branca develop amb la tècnica triada.

    git checkout develop
    git merge --squash --ff-only feature/nom-funcionalitat
    git commit
    
    git checkout develop
    git merge --no-ff feature/nom-funcionalitat
    
    git checkout develop
    git merge --ff-only feature/nom-funcionalitat
    
    git checkout develop
    git merge --no-ff feature/nom-funcionalitat
    
  5. Publica els canvis de la branca develop en el repositori remot amb git push.

    És possible que, mentre feies aquest procés, altres persones hagen publicat canvis nous en la branca develop.

    En aquest cas, la teua branca develop no està actualitzada i no es pot publicar. Cal tornar la branca develop a l'estat del repositori remot i repetir el procés d'integració.

    git checkout develop
    git reset --hard origin/develop
    
  6. Elimina la branca feature/* del repositori local i del remot.

    git branch -D feature/nom-funcionalitat
    git push -d origin feature/nom-funcionalitat
    

merge --no-ff

Gitflow és una de les estratègies de ramificació més conegudes i utilitzades en projectes de desenvolupament de programari. Es basa en la creació de les branques main, develop, feature/*, release/* i hotfix/*.

Esquema de branques amb Gitflow

Figura 3. Esquema de branques amb Gitflow.

La particularitat d'aquesta estratègia és que les branques de funcionalitat feature/* es fusionen amb la branca de desenvolupament develop mitjançant merge --no-ff. D'aquesta manera, es conserva la història de les branques de funcionalitat, que s'incorporen amb un commit de fusió.

git checkout develop
git merge --no-ff feature/A

Fusió de branques mitjançant merge --no-ff Fusió de branques mitjançant merge --no-ff

Figura 4. Fusió de branques mitjançant merge --no-ff.

Les característiques d'aquesta opció són:

  • Històric complet: manté tot l'històric de canvis1.
  • Història no lineal: els commits de fusió fan que la història no siga lineal.
  • Fàcil de revertir: per a revertir una funcionalitat, només cal revertir un únic commit.
  • Difícil de seguir: en projectes amb moltes funcionalitats, la història pot ser difícil de seguir.

rebase + merge --ff-only

Aquesta tècnica es basa a fer un canvi de base (rebase) de la branca de funcionalitat per a després fusionar-la de manera lineal amb merge --ff-only.

git checkout feature/A
git rebase develop
git checkout develop
git merge --ff-only feature/A

Fusió de branques mitjançant rebase Fusió de branques mitjançant rebase

Figura 5. Fusió de branques mitjançant rebase + merge --ff-only.

Les característiques d'aquesta opció són:

  • Històric complet: manté tot l'històric de canvis1.
  • Història lineal: els commits s'apliquen un darrere de l'altre.
  • Conflictes complicats: el canvi de base de funcionalitats amb molts commits pot ser complicat quan hi ha conflictes.
  • Difícil de revertir: revertir una funcionalitat no és trivial, ja que cal revertir diversos commits.

rebase + merge --no-ff

Aquesta tècnica combina les dues anteriors per a aprofitar els avantatges de cadascuna i, alhora, minimitzar-ne els inconvenients. Consisteix a fer un canvi de base (rebase) i, després, fusionar la branca de funcionalitat mitjançant un commit de fusió amb merge --no-ff.

git checkout feature/A
git rebase develop
git checkout develop
git merge --no-ff feature/A

Fusió de branques mitjançant rebase + merge --no-ff Fusió de branques mitjançant rebase + merge --no-ff

Figura 6. Fusió de branques mitjançant rebase + merge --no-ff.

Les característiques d'aquesta opció són:

  • Històric complet: manté tot l'històric de canvis1.
  • Història semilineal: la història queda neta i les funcionalitats s'integren una després de l'altra.
  • Fàcil de revertir: per a revertir una funcionalitat, només cal revertir un únic commit.
  • Conflictes complicats: el canvi de base de funcionalitats amb molts commits pot ser complicat.

merge --squash --ff-only

Aquesta tècnica consisteix a fusionar les branques de funcionalitat amb la branca de desenvolupament develop mitjançant merge --squash --ff-only, de manera que tots els commits de la branca de funcionalitat es fusionen en un únic commit.

Aquesta és la tècnica d'integració recomanada.

git checkout develop
git merge --squash --ff-only feature/A
git commit -m <missatge>

Fusió de branques mitjançant merge --squash --ff-only Fusió de branques mitjançant merge --squash --ff-only

Figura 7. Fusió de branques mitjançant merge --squash --ff-only.

Si la branca de funcionalitat no està actualitzada respecte de la branca de desenvolupament, es considera una bona pràctica integrar primer els canvis de develop en la branca de funcionalitat. A més, en aquest procés es poden resoldre els conflictes, si n'hi ha. Per a fer aquesta integració, es recomana utilitzar git merge --no-ff:

git checkout feature/A
git merge --no-ff develop
git checkout develop
git merge --squash --ff-only feature/A
git commit -m <missatge>

Fusió de branques mitjançant merge --no-ff + merge --squash --ff-only Fusió de branques mitjançant merge --no-ff + merge --squash --ff-only

Figura 8. Fusió de branques mitjançant merge --no-ff + merge --squash --ff-only.

Com que la branca de funcionalitat s'elimina després de la fusió, no importa si la seua història queda neta o no.

Les característiques d'aquesta opció són:

  • Històric parcial: no manté tot l'històric de canvis1.
  • Història lineal: cada funcionalitat ocupa un únic commit en la branca develop.
  • Fàcil de revertir: per a revertir una funcionalitat, només cal revertir un únic commit.
  • Revisió senzilla: facilita la revisió de codi, ja que tots els canvis es troben en un únic commit.
  • Menys soroll: evita la sobrecàrrega de commits en la branca de desenvolupament develop.
  • Llibertat en la branca de funcionalitat: l'equip de desenvolupament no s'ha de preocupar de com queda la història de la branca de funcionalitat i pot fer micro-commits, ja que desapareixen quan la branca s'esborra després d'integrar-la.

Branques de llançament

Les branques de llançament són branques temporals que s'utilitzen per a preparar el llançament d'una versió. Normalment, el seu prefix és release/.

Aquestes branques s'utilitzen per a fer tasques específiques de preparació del llançament, com ara:

  • Versió: actualitzar la versió del projecte.
  • Configuració: preparar paràmetres de configuració específics per al llançament.

Si el teu projecte no necessita tasques específiques per a preparar el llançament, pots prescindir d'aquestes branques i fusionar directament la branca de desenvolupament develop amb la branca principal main.

En aquestes branques es treballa de la mateixa manera que en qualsevol branca de funcionalitat. L'única diferència és que també cal integrar els canvis en la branca principal. El flux de treball amb aquestes branques és el següent:

  1. Es creen a partir de la branca de desenvolupament develop.
  2. Es fan les tasques de preparació del llançament.
  3. S'integren els canvis en la branca de desenvolupament develop.
  4. S'integren els canvis en la branca principal main.

La integració de la branca release/* en main i develop depén de la tècnica d'integració triada.

Branques de llançament Branques de llançament

Figura 9. Branques de llançament.

Branques de correcció

Les branques de correcció són branques temporals que s'utilitzen per a corregir errors crítics en el codi estable del projecte, quan la correcció no pot esperar a la versió següent. Normalment, el seu prefix és hotfix/.

Aquestes branques només s'han d'utilitzar per a corregir errors crítics que afecten la versió publicada del projecte i que s'han de corregir immediatament.

Aquestes branques poden dificultar el flux de treball, sobretot si es vol mantindre una història lineal del projecte.

El flux de treball amb aquestes branques és el següent:

  1. Es creen a partir de la branca principal main.
  2. Es fan les correccions necessàries.
  3. S'integren els canvis en la branca de desenvolupament develop.
  4. S'integren els canvis en la branca principal main.

Branques de correcció Branques de correcció

Figura 10. Branques de correcció.

Bibliografia


  1. Segons el punt de vista, mantindre l'històric de tots els commits pot ser un avantatge o un inconvenient. ↩↩↩↩

📌 Aquest document pot quedar desactualitzat després d’imprimir-lo. Pots consultar la versió més recent a la pàgina web.
🌿 Abans d’imprimir aquest document, considera si és realment necessari. Redueix el consum de paper i ajuda a protegir el nostre entorn.