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.
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/*.
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.
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.
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/...
Figura 2. Branques de funcionalitat.
El flux de treball amb aquestes branques és el següent:
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.
S'integren en la branca develop una vegada s'han implementat i provat els canvis.
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.
Per a integrar les funcionalitats en la branca de desenvolupament develop, se segueix aquest procés:
Sincronitza l'estat del repositori local amb el remot.
gitfetch
Actualitza la branca local develop amb els canvis del remot amb git pull.
gitcheckoutdevelop
gitpull--ff-only# (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).
gitconfig[--global]pull.ffonly
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.
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ó.
gitcheckoutdevelop
gitreset--hardorigin/develop
Elimina la branca feature/* del repositori local i del remot.
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/*.
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ó.
gitcheckoutdevelop
gitmerge--no-fffeature/A
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.
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.
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.
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:
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.
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:
Es creen a partir de la branca de desenvolupament develop.
Es fan les tasques de preparació del llançament.
S'integren els canvis en la branca de desenvolupament develop.
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.
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:
Es creen a partir de la branca principal main.
Es fan les correccions necessàries.
S'integren els canvis en la branca de desenvolupament develop.
S'integren els canvis en la branca principal main.