Les branques són una de les característiques més importants de Git,
que permet el desenvolupament col·laboratiu i en paral·lel d'un projecte.
Moltes de les estratègies de Git per desenvolupar projectes
es basen a realitzar els canvis en branques independents que,
una vegada acabades, s'integren en la branca principal.
La branca principal d'un projecte originalment s'anomenava master,
però últimament és preferible utilitzar el nom main
per evitar la nomenclatura master/slave (amo/esclau),
que té connotacions racistes.
Informació
S'ha canviat el nom de la branca principal de master a main per a seguir les recomanacions de la comunitat de desenvolupament.
Una branca és una línia de desenvolupament independent.
En Git, una branca és un simple punter a un commit,
que avança a mesura que es fan nous commits sobre aquesta.
Exemple: Estructura de branques inicial
L'estructura de branques inicial del repositori
que utilitzarem en aquest material és la següent
és el que es mostra a la Figura 1.
Originalment, s'utilitzava l'ordre git checkout per canviar de branca,
però com que aquesta ordre té moltes altres funcions,
s'ha introduït l'ordre git switch a partir de la versió 2.23 de Git
per evitar confusions.
En qualsevol cas, canviar de branca significa moure el punter HEAD a la branca desitjada.
El canvi de branca també implica modificar el contingut del Directori de Treball a l'estat
del commit al qual apunta la branca.
La Figura 2 mostra l'estat del repositori quan el HEAD apunta a la branca main.
La Figura 3 mostra l'estat del repositori després de canviar a la branca menjar.
Exemple: Canviar a la branca menjar
L'estat del repositori abans de canviar a la branca menjar és el següent:
jpuigcerver@fp:~/git_branques(main)$catREADME.md
# Branques a Gitjpuigcerver@fp:~/git_branques(main)$gitbranch
beguda* main menjar netejajpuigcerver@fp:~/git_branques(main)$gitlga
*ec4c927-(0 seconds ago)Commit inicial-Joan Puigcerver(HEAD -> main, neteja, menjar, beguda)
Després de canviar de branca, vegem que HEAD s'ha desplaçat a la branca menjar.
No obstant això, l'estat del repositori és el mateix ja que les dues branques apunten al mateix commit.
[-f | --force]: Opcional. Força l'eliminació de la branca.
[-D]: Opcional. Abreviatura de --delete --force.
Advertència
Quan un commit perd totes les referències per a ser accedit,
es diu que és un commitorfe i serà eliminat pel
recol·lector de brossa (garbage collector) de Git.
L'eliminació d'una branca pot provocar la pèrdua de commits.
En aquest cas, Git mostrarà un error i no es podrà eliminar la branca
a no ser que s'utilitze l'opció -D o --delete --force.
Exemple: Eliminació de la branca neteja
Tractem d'eliminar la branca neteja amb l'opció -d.
Com que aquests canvis no han segut fusionats,
l'eliminació de la branca provocarà la pèrdua dels commit realitzats.
Per poder esborrar-la, caldrà utilitzar l'opció -D.
jpuigcerver@fp:~/git_branques(neteja)$gitcheckoutmain
Switched to branch 'main'jpuigcerver@fp:~/git_branques(main)$gitlga
*7540d67-(0 seconds ago)Beguda-Joan Puigcerver(beguda)| *a7ef672-(0 seconds ago)Menjar-Joan Puigcerver(menjar)|/ | *06d7d94-(0 seconds ago)Neteja-Joan Puigcerver(neteja)|/ *ec4c927-(0 seconds ago)Commit inicial-Joan Puigcerver(HEAD -> main)jpuigcerver@fp:~/git_branques(main)$gitbranch-dneteja
error: the branch 'neteja' is not fully mergedhint: If you are sure you want to delete it, run 'git branch -D neteja'hint: Disable this message with "git config advice.forceDeleteBranch false"jpuigcerver@fp:~/git_branques(main)$gitbranch-Dneteja
Deleted branch neteja (was 06d7d94).jpuigcerver@fp:~/git_branques(main)$gitlga
*7540d67-(0 seconds ago)Beguda-Joan Puigcerver(beguda)| *a7ef672-(0 seconds ago)Menjar-Joan Puigcerver(menjar)|/ *ec4c927-(0 seconds ago)Commit inicial-Joan Puigcerver(HEAD -> main)
La branca neteja ha estat eliminada i, per tant, el commitProductes de neteja
s'ha convertit en un commit orfe i serà eliminat pel recol·lector de brossa de Git.
La fusió directa (fast-forward) és un tipus de fusió que es produeix
quan la branca actual (HEAD) no té cap nou commit des de que es va crear la branca
que es vol fusionar. És a dir, la branca que es vol fusionar està darrere de la branca actual,
mantenint una història lineal.
Aquesta fusió consisteix a avançar el punter de la branca actual (HEAD)
fins on es troba la branca que es vol fusionar.
Important
La fusió directa és la forma més senzilla i neta de fusionar branques,
ja que no es crea cap commit addicional per fusionar les branques i
manté una història lineal i fàcil de seguir.
Informació
Per assegurar-se que la fusió siga directa, es pot utilitzar l'opció --ff-only
En cas que la fusió no siga directa, Git mostrarà un error i no es realitzarà la fusió.
Git pot ser configurat per sempre tractar de realitzar una fusió directa:
gitconfig--globalmerge.ffonly
Exemple: Fusió directa de la branca menjar a la branca main
Partint de la situació de la Figura 5, on la branca menjar té un commit més que la branca main,
la fusió de la branca menjar a la branca main serà una fusió directa.
En aquest cas, la fusió es portarà a terme avançant el punter de la branca main fins al punt on es troba la branca docs,
tal i com es mostra en la Figura 6.
No sempre és possible realitzar fusió mitjançant una fusió directa (fast-forward).
Pot donar-se el cas que les dues branques hagen divergit, és a dir, que cada branca haja
realitzat canvis que no estan presents en l'altra branca.
En aquest cas, la fusió es realitza mitjançant un commit de fusió (merge commit).
Aquest commit de fusió té dos pares, un per cada branca que es fusiona
i incorpora els canvis de les dues branques.
En el moment de realitzar la fusió (git merge),
Git crearà un nou commit de fusió que incorporarà els canvis de les dues branques.
Aquest commit necessita d'un missatge, per tant, es pot utilitzar l'opció -m per afegir un missatge al commit.
Si no se n'especifica cap, s'obrirà l'editor de text configurat per defecte per a afegir el missatge.
(Vegeu Confirmar canvis (git commit))
Advertència
Aquest tipus de fusió no és tan neta com la fusió directa (fast-forward),
ja que la història del projecte es torna més complexa i no lineal.
Informació
Per forçar que la fusió es realitze amb un commit de fusió (merge commit),
es pot utilitzar l'opció --no-ff.
Git pot ser configurat per sempre realitze una fusió amb un commit de fusió (merge commit):
gitconfig--globalmerge.ffno
Exemple: Fusió de branques divergents beguda a la branca main
Partim de la situació de la Figura 8, on la branca beguda ha divergit de la branca main.
Realitzem la fusió de la branca beguda sobre la branca `main.
Després ens ens trobem en la situació mostrada en la Figura 9,
on s'ha creat un commit de fusió que incorpora els canvis de la branca beguda a la branca main.
jpuigcerver@fp:~/git_branques(main)$gitcheckoutmain
Already on 'main'jpuigcerver@fp:~/git_branques(main)$gitmergebeguda--no-ff-m"Merge branch 'beguda' into 'main'"# (1)!Merge made by the 'ort' strategy. beguda.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 beguda.txtjpuigcerver@fp:~/git_branques(main)$ls# (2)!beguda.txtmenjar.txtREADME.mdjpuigcerver@fp:~/git_branques(main)$catbeguda.txt
AiguaOrxatajpuigcerver@fp:~/git_branques(main)$gitlga
*d71a079-(0 seconds ago)Merge branch 'beguda' into 'main'-Joan Puigcerver(HEAD -> main)|\ | *7540d67-(0 seconds ago)Beguda-Joan Puigcerver(beguda)* |a7ef672-(0 seconds ago)Menjar-Joan Puigcerver(menjar)|/ *ec4c927-(0 seconds ago)Commit inicial-Joan Puigcerver
La opció -m permet afegir un missatge al commit de fusió.
Si no s'especifica, s'obrirà l'editor de text per a afegir el missatge.
Vegem com s'hagen incorporat els canvis de la branca beguda a la branca main.
En el procés de fusió de branques, pot donar-se el cas que les dues branques hagen modificat
la mateixa part d'un fitxer. En aquest cas, Git no pot resoldre el conflictes de forma automàtica
i caldrà resoldre'ls manualment.
En el moment que executem git merge, Git detectarà el conflictes els marcarà en el fitxer
amb la següent notació:
<<<<<<< HEAD
Contingut de la branca actual
=======
Contingut de la branca a fusionar
>>>>>>> branca_a_fusionar
A més, el repositori passarà a l'estat de fusió (MERGING), indicant que hi ha una
fusió en curs.
Per resoldre el conflicte, caldrà:
Editar el fitxer i resoldre manualment el conflicte.
Esborrar les marques de conflicte.
Una vegada resolt el conflicte, caldrà confirmar els canvis amb git add i git commit.
Informació
En cas que es desitge cancel·lar el procés de fusió, es pot utilitzar l'ordre git merge --abort.
Preparació branques divergents fruta i verdura
Creem les branques fruta i verdura des de la branca main.
Afegim dues fruites al fitxer menjar.txt en la branca fruta.
jpuigcerver@fp:~/git_branques(main)$gitcheckoutfruta
Switched to branch 'fruta'jpuigcerver@fp:~/git_branques(fruta)$echo"Pomes">>menjar.txt
jpuigcerver@fp:~/git_branques(fruta)$echo"Bresquilles">>menjar.txt
jpuigcerver@fp:~/git_branques(fruta)$gitstatus
On branch frutaChanges not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory)modified: menjar.txtno changes added to commit (use "git add" and/or "git commit -a")jpuigcerver@fp:~/git_branques(fruta)$gitaddmenjar.txt
jpuigcerver@fp:~/git_branques(fruta)$gitcommit-m"Fruta"[fruta d32e3ba] Fruta 1 file changed, 2 insertions(+)jpuigcerver@fp:~/git_branques(fruta)$gitlga
*d32e3ba-(1 second ago)Fruta-Joan Puigcerver(HEAD -> fruta)*d71a079-(1 second ago)Merge branch 'beguda' into 'main'-Joan Puigcerver(verdura, main)|\ | *7540d67-(1 second ago)Beguda-Joan Puigcerver(beguda)* |a7ef672-(1 second ago)Menjar-Joan Puigcerver(menjar)|/ *ec4c927-(1 second ago)Commit inicial-Joan Puigcerver
Afegim dues verdures al fitxer menjar.txt en la branca verdura.
jpuigcerver@fp:~/git_branques(fruta)$gitcheckoutverdura
Switched to branch 'verdura'jpuigcerver@fp:~/git_branques(verdura)$echo"Tomaques">>menjar.txt
jpuigcerver@fp:~/git_branques(verdura)$echo"Creïlles">>menjar.txt
jpuigcerver@fp:~/git_branques(verdura)$gitstatus
On branch verduraChanges not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory)modified: menjar.txtno changes added to commit (use "git add" and/or "git commit -a")jpuigcerver@fp:~/git_branques(verdura)$gitaddmenjar.txt
jpuigcerver@fp:~/git_branques(verdura)$gitcommit-m"Verdura"[verdura e4cfd4d] Verdura 1 file changed, 2 insertions(+)jpuigcerver@fp:~/git_branques(verdura)$gitlga
*e4cfd4d-(0 seconds ago)Verdura-Joan Puigcerver(HEAD -> verdura)| *d32e3ba-(1 second ago)Fruta-Joan Puigcerver(fruta)|/ *d71a079-(1 second ago)Merge branch 'beguda' into 'main'-Joan Puigcerver(main)|\ | *7540d67-(1 second ago)Beguda-Joan Puigcerver(beguda)* |a7ef672-(1 second ago)Menjar-Joan Puigcerver(menjar)|/ *ec4c927-(1 second ago)Commit inicial-Joan Puigcerver
Exemple: Fusió de branques fruta i verdura amb conflictes
Les branques fruta i verdura han modificat la mateixa secció del fitxer menjar.txt,
per tant, quan les fusionem, es produirà un conflicte.
jpuigcerver@fp:~/git_branques(verdura)$gitcheckoutmain
Switched to branch 'main'jpuigcerver@fp:~/git_branques(main)$gitlg
*d71a079-(1 second ago)Merge branch 'beguda' into 'main'-Joan Puigcerver(HEAD -> main)|\ | *7540d67-(1 second ago)Beguda-Joan Puigcerver(beguda)* |a7ef672-(1 second ago)Menjar-Joan Puigcerver(menjar)|/ *ec4c927-(1 second ago)Commit inicial-Joan Puigcerverjpuigcerver@fp:~/git_branques(main)$gitshowfruta
commit d32e3bac4fc8792f3c13805d983a926c72841fb5Author: Joan Puigcerver <j.puigcerveribanez@edu.gva.es>Date: Tue Oct 29 16:23:59 2024 +0100 Frutadiff --git a/menjar.txt b/menjar.txtindex c8d5456..312ebef 100644--- a/menjar.txt+++ b/menjar.txt@@ -1,2 +1,4 @@Pa
Macarrons
+Pomes+Bresquillesjpuigcerver@fp:~/git_branques(main)$gitshowverdura
commit e4cfd4d48cb94e0e87c573f14f6eed310bb12750Author: Joan Puigcerver <j.puigcerveribanez@edu.gva.es>Date: Tue Oct 29 16:24:00 2024 +0100 Verduradiff --git a/menjar.txt b/menjar.txtindex c8d5456..03fb9ab 100644--- a/menjar.txt+++ b/menjar.txt@@ -1,2 +1,4 @@Pa
Macarrons
+Tomaques+Creïlles
Primer, realitzem una Fusió directa entre de la branca fruta a la branca main,
on no hi haurà conflictes.
jpuigcerver@fp:~/git_branques(main)$gitmergefruta
Updating d71a079..d32e3baFast-forward menjar.txt | 2 ++ 1 file changed, 2 insertions(+)jpuigcerver@fp:~/git_branques(main)$gitlga
*e4cfd4d-(0 seconds ago)Verdura-Joan Puigcerver(verdura)| *d32e3ba-(1 second ago)Fruta-Joan Puigcerver(HEAD -> main, fruta)|/ *d71a079-(1 second ago)Merge branch 'beguda' into 'main'-Joan Puigcerver|\ | *7540d67-(1 second ago)Beguda-Joan Puigcerver(beguda)* |a7ef672-(1 second ago)Menjar-Joan Puigcerver(menjar)|/ *ec4c927-(1 second ago)Commit inicial-Joan Puigcerver
Després, realitzarem la fusió de la branca verdura a la branca main,
on es produirà un conflicte.
jpuigcerver@fp:~/git_branques(main)$gitmergeverdura
Auto-merging menjar.txtCONFLICT (content): Merge conflict in menjar.txtAutomatic merge failed; fix conflicts and then commit the result.jpuigcerver@fp:~/git_branques(main|MERGING)$gitstatus
On branch mainYou have unmerged paths. (fix conflicts and run "git commit") (use "git merge --abort" to abort the merge)Unmerged paths: (use "git add <file>..." to mark resolution)both modified: menjar.txtno changes added to commit (use "git add" and/or "git commit -a")jpuigcerver@fp:~/git_branques(main|MERGING)$catmenjar.txt
PaMacarrons<<<<<<< HEADPomesBresquilles=======TomaquesCreïlles>>>>>>> verdura
En aquest cas, resoldrem els conflictes eliminant les marques de conflicte i
mantenint els canvis de les dues branques.
Després, marcarem els fitxers com a resolts amb git add i confirmarem els canvis amb git commit.
Pa
Macarrons
<<<<<<< HEAD
Pomes
Bresquilles
=======
Tomaques
Creïlles
>>>>>>> verdura
Pa
Macarrons
Pomes
Bresquilles
Tomaques
Creïlles
jpuigcerver@fp:~/git_branques(main|MERGING)$codemenjar.txt# (1)!jpuigcerver@fp:~/git_branques(main|MERGING)$gitaddmenjar.txt
jpuigcerver@fp:~/git_branques(main|MERGING)$gitstatus
On branch mainAll conflicts fixed but you are still merging. (use "git commit" to conclude merge)Changes to be committed:modified: menjar.txtjpuigcerver@fp:~/git_branques(main|MERGING)$gitcommit-m"Merge branch 'verdura' into 'main'"[main ce43962] Merge branch 'verdura' into 'main'jpuigcerver@fp:~/git_branques(main)$gitlga
*ce43962-(0 seconds ago)Merge branch 'verdura' into 'main'-Joan Puigcerver(HEAD -> main)|\ | *e4cfd4d-(0 seconds ago)Verdura-Joan Puigcerver(verdura)* |d32e3ba-(1 second ago)Fruta-Joan Puigcerver(fruta)|/ *d71a079-(1 second ago)Merge branch 'beguda' into 'main'-Joan Puigcerver|\ | *7540d67-(1 second ago)Beguda-Joan Puigcerver(beguda)* |a7ef672-(1 second ago)Menjar-Joan Puigcerver(menjar)|/ *ec4c927-(1 second ago)Commit inicial-Joan Puigcerver
Editem el fitxer menjar.txt per eliminar les marques de conflicte i mantenir els canvis de les dues branques.
El canvi de base (rebase) és una altra manera de fusionar canvis de branques divergents,
que consisteix en aplicar els canvis dels commit d'una branca sobre una altra branca, en ordre cronològic.
Important
Aquesta tècnica permet eliminar les branques divergents i mantindre una història lineal.
En aquest cas, caldrà resoldre els conflictes per a cada commit en el que s'està canviant la base.
Després de resoldre els conflictes d'un commit, caldrà continuar amb el procés de rebase amb l'ordre git rebase --continue
fins que s'haja aplicat el canvi de base a tots els commit de la branca.
Preparació branques divergents postre i aperitiu
Creem les branques postre i aperitiu des de la branca main.
Exemple: Fusió de les branques postre i aperitiu amb canvi de base amb conflictes
Les branques postre i aperitiu han modificat la mateixa secció del fitxer menjar.txt,
per tant, quan realitzem el canvi de base es produiran conflictes.
La branca aperitiu ha divergit de la branca main, i per tal de mantindre una història lineal,
canviarem la base de la branca paella a la branca main.
Com que han s'ha modificat la mateixa secció del fitxer menjar.txt,
es produiran conflictes.
jpuigcerver@fp:~/git_branques(main)$gitcheckoutaperitiu
Switched to branch 'aperitiu'jpuigcerver@fp:~/git_branques(aperitiu)$gitrebasemain
Auto-merging menjar.txtCONFLICT (content): Merge conflict in menjar.txtRebasing (1/1)error: could not apply f3c59a0... Menjar aperitiuhint: Resolve all conflicts manually, mark them as resolved withhint: "git add/rm <conflicted_files>", then run "git rebase --continue".hint: You can instead skip this commit: run "git rebase --skip".hint: To abort and get back to the state before "git rebase", run "git rebase --abort".hint: Disable this message with "git config advice.mergeConflict false"Could not apply f3c59a0... Menjar aperitiujpuigcerver@fp:~/git_branques(aperitiu|REBASE 1/1)$gitstatus
interactive rebase in progress; onto 0d71e2bLast command done (1 command done): pick f3c59a0 Menjar aperitiuNo commands remaining.You are currently rebasing branch 'aperitiu' on '0d71e2b'. (fix conflicts and then run "git rebase --continue") (use "git rebase --skip" to skip this patch) (use "git rebase --abort" to check out the original branch)Unmerged paths: (use "git restore --staged <file>..." to unstage) (use "git add <file>..." to mark resolution)both modified: menjar.txtno changes added to commit (use "git add" and/or "git commit -a")
En aquest cas, resoldrem els conflictes eliminant les marques de conflicte i
mantenint els canvis de les dues branques.
Després, marcarem els fitxers com a resolts amb git add i continuarem amb el procés de rebase.
Pa
Macarrons
Pomes
Bresquilles
Tomaques
Creïlles
Arròs
Pollastre
<<<<<<< HEAD
Iogurt
=======
Olives
>>>>>>> f3c59a0 (Menjar aperitiu)
Pa
Macarrons
Pomes
Bresquilles
Tomaques
Creïlles
Arròs
Pollastre
Iogurt
Olives
jpuigcerver@fp:~/git_branques(aperitiu|REBASE 1/1)$codemenjar.txt# (1)!jpuigcerver@fp:~/git_branques(aperitiu|REBASE 1/1)$gitaddmenjar.txt
jpuigcerver@fp:~/git_branques(aperitiu|REBASE 1/1)$gitstatus
interactive rebase in progress; onto 0d71e2bLast command done (1 command done): pick f3c59a0 Menjar aperitiuNo commands remaining.You are currently rebasing branch 'aperitiu' on '0d71e2b'. (all conflicts fixed: run "git rebase --continue")Changes to be committed: (use "git restore --staged <file>..." to unstage)modified: menjar.txtjpuigcerver@fp:~/git_branques(aperitiu|REBASE 1/1)$gitrebase--continue
[detached HEAD 0a53b4b] Menjar aperitiu Author: Joan Puigcerver <j.puigcerveribanez@edu.gva.es> 1 file changed, 1 insertion(+)Successfully rebased and updated refs/heads/aperitiu.jpuigcerver@fp:~/git_branques(aperitiu)$gitlga-3
*0a53b4b-(0 seconds ago)Menjar aperitiu-Joan Puigcerver(HEAD -> aperitiu)*0d71e2b-(0 seconds ago)Menjar postre-Joan Puigcerver(postre, main)*8c43498-(0 seconds ago)Menjar paella-Joan Puigcerver(paella)
Després del canvi de base, es possible fusionar la branca apeririu a la branca main amb una fusió directa (fast-forward).