Salta el contingut
 

Branques

Autor: Joan Puigcerver Ibáñez

Correu electrònic: j.puigcerveribanez@edu.gva.es

Llicència: CC BY-NC-SA 4.0

(Reconeixement - NoComercial - CompartirIgual) 🅭

Introducció

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.

Vegeu: Regarding Git and Branch Naming

Preparació repositori d'exemple

En aquest material treballarem sobre un nou repositori local.

Inicialització:

#!/bin/bash

# Elimina els repositori si existeix
if [ -d ~/git_branques ]; then
    rm -rf ~/git_branques
fi

mkdir -p ~/git_branques
cd ~/git_branques
git init
git branch -m main # (1)!
echo "# Branques a Git" > README.md
git add README.md
git commit -m "Commit inicial"
git lga

jpuigcerver@fp:~ $ mkdir -p ~/git_branques
jpuigcerver@fp:~ $ cd ~/git_branques
jpuigcerver@fp:~/git_branques $ git init
Initialized empty Git repository in ~/git_branques/.git/
jpuigcerver@fp:~/git_branques (main) $ git branch -m main # (1)!
jpuigcerver@fp:~/git_branques (main) $ echo "# Branques a Git" > README.md
jpuigcerver@fp:~/git_branques (main) $ git add README.md
jpuigcerver@fp:~/git_branques (main) $ git commit -m "Commit inicial"
[main (root-commit) ec4c927] Commit inicial
 1 file changed, 1 insertion(+)
 create mode 100644 README.md
jpuigcerver@fp:~/git_branques (main) $ git lga
* ec4c927 - (0 seconds ago) Commit inicial - Joan Puigcerver (HEAD -> main)
  1. Canviem el nom de la branca principal a main.

Branques

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.

Estructura de branques

Figura 1. Estructura de branques inicial.

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.

jpuigcerver@fp:~/git_branques (main) $ git lga
* ec4c927 - (0 seconds ago) Commit inicial - Joan Puigcerver (HEAD -> main)

Vegem que tenim un únic commit on està situada l'única branca que existeix, anomenada main.

També observem que el HEAD apunta a la branca main, indicant que és la branca activa i que el Directori de Treball es troba en aquest estat.

El HEAD es veurà amb més detall a l'apartat Canviar de branca.

La ordre git branch ens permet veure i manipular les branques d'un repositori.

Documentació

Documentació oficial de l'ordre git branch

Mostrar les branques

Per mostrar les branques d'un repositori, utilitzem l'ordre:

git branch [--list] [-a | --all] [-v | --verbose]
L'ordre git branch mostra les branques si:

  • No s'utilitza cap opció.
  • S'utilitza l'opció --list.

Més opcions:

  • [-a | --all]: Opcional. Mostra totes les branques, incloent les remotes (es veurà en els apunts Remots).
  • [-v | --verbose]: Opcional. Mostra més informació de cada branca.
Exemple: Mostrar les branques

Mostrem les branques del nostre repositori.

jpuigcerver@fp:~/git_branques (main) $ git branch
* main

Vegem que tenim una única branca anomenada main.

Aquesta ordre indicarà amb un * la branca activa (on es troba el HEAD).

Crear una branca

Per crear una nova branca, utilitzem l'ordre:

git branch [-f | --force] <nom>

  • [-f | --force]: Opcional. Força la creació de la branca.
  • <nom>: Nom de la nova branca.

Advertència

Si ja existeix una branca amb el mateix nom i no s'utilitza l'opció -f o --force, l'ordre mostrarà un error i no es crearà la branca.

Exemple: Creació de les branques menjar, beguda i neteja

Creem les branca menjar, beguda i neteja on es realitzaran canvis relacionats amb una llista de la compra.

jpuigcerver@fp:~/git_branques (main) $ git lga
* ec4c927 - (0 seconds ago) Commit inicial - Joan Puigcerver (HEAD -> main)
jpuigcerver@fp:~/git_branques (main) $ git branch menjar
jpuigcerver@fp:~/git_branques (main) $ git branch beguda
jpuigcerver@fp:~/git_branques (main) $ git branch neteja
jpuigcerver@fp:~/git_branques (main) $ git branch
  beguda
* main
  menjar
  neteja
jpuigcerver@fp:~/git_branques (main) $ git lga
* ec4c927 - (0 seconds ago) Commit inicial - Joan Puigcerver (HEAD -> main, neteja, menjar, beguda)

Vegem que s'ha creat les branques s'han creat al mateix commit on estàvem situats.

No obstant això, la branca activa (HEAD) continua sent main.

Estructura de branques després de crear les branques

Figura 2. Estructura de branques després de crear les branques menjar, beguda i neteja.

Canviar de branca

Existeixen dues ordres per canviar de branca, cadascuna amb la seva pròpia sintaxi i opcions:

git checkout <nom>
git switch <nom>

Documentació

Informació

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.

Més informació: Stack Overflow: What's the difference between git switch and git checkout <branch>?

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) $ cat README.md
# Branques a Git
jpuigcerver@fp:~/git_branques (main) $ git branch
  beguda
* main
  menjar
  neteja
jpuigcerver@fp:~/git_branques (main) $ git lga
* 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.

jpuigcerver@fp:~/git_branques (main) $ git checkout menjar
Switched to branch 'menjar'
jpuigcerver@fp:~/git_branques (menjar) $ cat README.md
# Branques a Git
jpuigcerver@fp:~/git_branques (menjar) $ git branch
  beguda
  main
* menjar
  neteja
jpuigcerver@fp:~/git_branques (menjar) $ git lga
* ec4c927 - (0 seconds ago) Commit inicial - Joan Puigcerver (HEAD -> menjar, neteja, main, beguda)

Estructura de branques després de canviar a la branca menjar

Figura 3. Estructura de branques després de canviar a la branca menjar.

Canvis en una branca

Per fer canvis en una branca cal:

  • Situar-se en la branca on es vol fer el canvi (git checkout o git switch).
  • Realitzar els canvis desitjats.
  • Confirmar els canvis amb git commit.

Quan es realitza un commit en una branca, el punter de la branca actual (HEAD) s'avança al nou commit.

Exemple: Canvis en la branca menjar

Anem a afegir dos productes al fitxer menjar.txt en la branca menjar.

jpuigcerver@fp:~/git_branques (menjar) $ echo "Pa" >> menjar.txt
jpuigcerver@fp:~/git_branques (menjar) $ echo "Macarrons" >> menjar.txt
jpuigcerver@fp:~/git_branques (menjar) $ git status
On branch menjar
Untracked files:
  (use "git add <file>..." to include in what will be committed)
    menjar.txt

nothing added to commit but untracked files present (use "git add" to track)
jpuigcerver@fp:~/git_branques (menjar) $ git add menjar.txt
jpuigcerver@fp:~/git_branques (menjar) $ git commit -m "Menjar"
[menjar a7ef672] Menjar
 1 file changed, 2 insertions(+)
 create mode 100644 menjar.txt
jpuigcerver@fp:~/git_branques (menjar) $ git lga
* a7ef672 - (0 seconds ago) Menjar - Joan Puigcerver (HEAD -> menjar)
* ec4c927 - (0 seconds ago) Commit inicial - Joan Puigcerver (neteja, main, beguda)

Vegem que la branca menjar i el HEAD han avançat al nou commit, mentre que les altres branques no han canviat i continuen apuntant al commit anterior.

Estructura de branques després de fer un commit a la branca menjar

Figura 4. Estructura de branques després de fer un commit a la branca menjar.

Exemple: Canvis en la branca beguda

Anem a afegir dos productes al fitxer beguda.txt en la branca beguda.

jpuigcerver@fp:~/git_branques (menjar) $ git checkout beguda
Switched to branch 'beguda'
jpuigcerver@fp:~/git_branques (beguda) $ echo "Aigua" >> beguda.txt
jpuigcerver@fp:~/git_branques (beguda) $ echo "Orxata" >> beguda.txt
jpuigcerver@fp:~/git_branques (beguda) $ git status
On branch beguda
Untracked files:
  (use "git add <file>..." to include in what will be committed)
    beguda.txt

nothing added to commit but untracked files present (use "git add" to track)
jpuigcerver@fp:~/git_branques (beguda) $ git add beguda.txt
jpuigcerver@fp:~/git_branques (beguda) $ git commit -m "Beguda"
[beguda 7540d67] Beguda
 1 file changed, 2 insertions(+)
 create mode 100644 beguda.txt
jpuigcerver@fp:~/git_branques (beguda) $ git lga
* 7540d67 - (0 seconds ago) Beguda - Joan Puigcerver (HEAD -> beguda)
| * a7ef672 - (0 seconds ago) Menjar - Joan Puigcerver (menjar)
|/  
* ec4c927 - (0 seconds ago) Commit inicial - Joan Puigcerver (neteja, main)

Vegem que la branca beguda i el HEAD han avançat al nou commit.

Estructura de branques després de fer un commit a la branca beguda

Figura 5. Estructura de branques després de fer un commit a la branca beguda.

Exemple: Canvis en la branca neteja

Anem a afegir dos productes al fitxer neteja.txt en la branca neteja.

jpuigcerver@fp:~/git_branques (beguda) $ git checkout neteja
Switched to branch 'neteja'
jpuigcerver@fp:~/git_branques (neteja) $ echo "Paper higiènic" >> neteja.txt
jpuigcerver@fp:~/git_branques (neteja) $ echo "Fregall" >> neteja.txt
jpuigcerver@fp:~/git_branques (neteja) $ git status
On branch neteja
Untracked files:
  (use "git add <file>..." to include in what will be committed)
    neteja.txt

nothing added to commit but untracked files present (use "git add" to track)
jpuigcerver@fp:~/git_branques (neteja) $ git add neteja.txt
jpuigcerver@fp:~/git_branques (neteja) $ git commit -m "Neteja"
[neteja 06d7d94] Neteja
 1 file changed, 2 insertions(+)
 create mode 100644 neteja.txt
jpuigcerver@fp:~/git_branques (neteja) $ git lga
* 7540d67 - (0 seconds ago) Beguda - Joan Puigcerver (beguda)
| * a7ef672 - (0 seconds ago) Menjar - Joan Puigcerver (menjar)
|/  
| * 06d7d94 - (0 seconds ago) Neteja - Joan Puigcerver (HEAD -> neteja)
|/  
* ec4c927 - (0 seconds ago) Commit inicial - Joan Puigcerver (main)

Vegem que la branca neteja i el HEAD han avançat al nou commit.

Estructura de branques després de fer un commit a la branca neteja

Figura 6. Estructura de branques després de fer un commit a la branca neteja.

Reanomenar una branca

Per reanomenar una branca, utilitzem l'ordre:

git branch [-m | --move] <nou_nom>

  • [-m | --move]: Opcional. Reanomena la branca actual (on es troba el HEAD).
  • <nou_nom>: Nou nom de la branca.

Exemple: Reanomenar la branca principal

En la Introducció s'ha utilitzat aquesta opció per canviar el nom de la branca principal de master a main.

git branch -m main

Eliminar una branca

Per eliminar una branca, utilitzem l'ordre:

git branch [-d | --delete] [-D] [-f | --force] <nom>

  • [-d | --delete]: Opcional. Elimina la branca.
  • [-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 commit orfe 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) $ git checkout main
Switched to branch 'main'
jpuigcerver@fp:~/git_branques (main) $ git lga
* 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) $ git branch -d neteja
error: the branch 'neteja' is not fully merged
hint: 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) $ git branch -D neteja
Deleted branch neteja (was 06d7d94).
jpuigcerver@fp:~/git_branques (main) $ git lga
* 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 commit Productes de neteja s'ha convertit en un commit orfe i serà eliminat pel recol·lector de brossa de Git.

Estructura de branques després d'eliminar la branca neteja

Figura 7. Estructura de branques després d'eliminar la branca neteja.

Fusió de branques (git merge)

La fusió de branques o merge és el procés de combinar els canvis d'una branca a una altra.

Aquest procés es realitza amb l'ordre:

git merge <branca>

  • <branca>: Nom de la branca que es vol fusionar amb la branca actual.

Important

La fusió de branques sempre incorpora els canvis de la branca indicada sobre la branca actual (on es troba el HEAD).

Segons l'estructura de les branques, la fusió pot ser directa (fast-forward) o mitjançant commit de fusió (merge commit).

Fusió directa

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.

Estructura de branques abans de la fusió directa

Figura 8. Estructura de branques abans de la fusió directa.

Aquesta fusió consisteix a avançar el punter de la branca actual (HEAD) fins on es troba la branca que es vol fusionar.

Estructura de branques després de la fusió directa

Figura 9. Estructura de branques després de la fusió directa de la branca menjar a la branca main.

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:

git config --global merge.ff only
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.

jpuigcerver@fp:~/git_branques (main) $ git checkout main
Already on 'main'
jpuigcerver@fp:~/git_branques (main) $ ls
README.md
jpuigcerver@fp:~/git_branques (main) $ git lga
* 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)

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.

jpuigcerver@fp:~/git_branques (main) $ git checkout main
Already on 'main'
jpuigcerver@fp:~/git_branques (main) $ git merge menjar
Updating ec4c927..a7ef672
Fast-forward
 menjar.txt | 2 ++
 1 file changed, 2 insertions(+)
 create mode 100644 menjar.txt
jpuigcerver@fp:~/git_branques (main) $ ls # (1)!
menjar.txt
README.md
jpuigcerver@fp:~/git_branques (main) $ cat menjar.txt
Pa
Macarrons
jpuigcerver@fp:~/git_branques (main) $ git lga
* 7540d67 - (0 seconds ago) Beguda - Joan Puigcerver (beguda)
| * a7ef672 - (0 seconds ago) Menjar - Joan Puigcerver (HEAD -> main, menjar)
|/  
* ec4c927 - (0 seconds ago) Commit inicial - Joan Puigcerver
  1. Vegem que el s'ha incorporat el fitxer menjar.txt amb els canvis de la branca menjar.

Fusió de branques divergents

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.

Història abans de la fusió en branques divergents

Figura 10. Història abans de la fusió en branques divergents.

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.

Història després de la fusió en branques divergents

Figura 11. Història després de la fusió en branques divergents de la branca beguda a la branca main.

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):

git config --global merge.ff no
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.

jpuigcerver@fp:~/git_branques (main) $ git checkout main
Already on 'main'
jpuigcerver@fp:~/git_branques (main) $ ls
menjar.txt
README.md
jpuigcerver@fp:~/git_branques (main) $ git lga
* 7540d67 - (0 seconds ago) Beguda - Joan Puigcerver (beguda)
| * a7ef672 - (0 seconds ago) Menjar - Joan Puigcerver (HEAD -> main, menjar)
|/  
* ec4c927 - (0 seconds ago) Commit inicial - Joan Puigcerver

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) $ git checkout main
Already on 'main'
jpuigcerver@fp:~/git_branques (main) $ git merge beguda --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.txt
jpuigcerver@fp:~/git_branques (main) $ ls # (2)!
beguda.txt
menjar.txt
README.md
jpuigcerver@fp:~/git_branques (main) $ cat beguda.txt
Aigua
Orxata
jpuigcerver@fp:~/git_branques (main) $ git lga
*   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
  1. 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.

  2. Vegem com s'hagen incorporat els canvis de la branca beguda a la branca main.

Resolució de conflictes

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à:

  1. Editar el fitxer i resoldre manualment el conflicte.
  2. 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
  1. Creem les branques fruta i verdura des de la branca main.
jpuigcerver@fp:~/git_branques (main) $ git checkout main
Already on 'main'
jpuigcerver@fp:~/git_branques (main) $ git branch fruta
jpuigcerver@fp:~/git_branques (main) $ git branch verdura
jpuigcerver@fp:~/git_branques (main) $ git lga
*   d71a079 - (0 seconds ago) Merge branch 'beguda' into 'main' - Joan Puigcerver (HEAD -> main, verdura, fruta)
|\  
| * 7540d67 - (0 seconds ago) Beguda - Joan Puigcerver (beguda)
* | a7ef672 - (0 seconds ago) Menjar - Joan Puigcerver (menjar)
|/  
* ec4c927 - (0 seconds ago) Commit inicial - Joan Puigcerver
  1. Afegim dues fruites al fitxer menjar.txt en la branca fruta.
jpuigcerver@fp:~/git_branques (main) $ git checkout fruta
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) $ git status
On branch fruta
Changes 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.txt

no changes added to commit (use "git add" and/or "git commit -a")
jpuigcerver@fp:~/git_branques (fruta) $ git add menjar.txt
jpuigcerver@fp:~/git_branques (fruta) $ git commit -m "Fruta"
[fruta d32e3ba] Fruta
 1 file changed, 2 insertions(+)
jpuigcerver@fp:~/git_branques (fruta) $ git lga
* 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
  1. Afegim dues verdures al fitxer menjar.txt en la branca verdura.
jpuigcerver@fp:~/git_branques (fruta) $ git checkout verdura
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) $ git status
On branch verdura
Changes 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.txt

no changes added to commit (use "git add" and/or "git commit -a")
jpuigcerver@fp:~/git_branques (verdura) $ git add menjar.txt
jpuigcerver@fp:~/git_branques (verdura) $ git commit -m "Verdura"
[verdura e4cfd4d] Verdura
 1 file changed, 2 insertions(+)
jpuigcerver@fp:~/git_branques (verdura) $ git lga
* 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) $ git checkout main
Switched to branch 'main'
jpuigcerver@fp:~/git_branques (main) $ git lg
*   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 Puigcerver
jpuigcerver@fp:~/git_branques (main) $ git show fruta
commit d32e3bac4fc8792f3c13805d983a926c72841fb5
Author: Joan Puigcerver <j.puigcerveribanez@edu.gva.es>
Date:   Tue Oct 29 16:23:59 2024 +0100

    Fruta

diff --git a/menjar.txt b/menjar.txt
index c8d5456..312ebef 100644
--- a/menjar.txt
+++ b/menjar.txt
@@ -1,2 +1,4 @@
 Pa
 Macarrons
+Pomes
+Bresquilles
jpuigcerver@fp:~/git_branques (main) $ git show verdura
commit e4cfd4d48cb94e0e87c573f14f6eed310bb12750
Author: Joan Puigcerver <j.puigcerveribanez@edu.gva.es>
Date:   Tue Oct 29 16:24:00 2024 +0100

    Verdura

diff --git a/menjar.txt b/menjar.txt
index c8d5456..03fb9ab 100644
--- a/menjar.txt
+++ b/menjar.txt
@@ -1,2 +1,4 @@
 Pa
 Macarrons
+Tomaques
+Creïlles
  1. Primer, realitzem una Fusió directa entre de la branca fruta a la branca main, on no hi haurà conflictes.
jpuigcerver@fp:~/git_branques (main) $ git merge fruta
Updating d71a079..d32e3ba
Fast-forward
 menjar.txt | 2 ++
 1 file changed, 2 insertions(+)
jpuigcerver@fp:~/git_branques (main) $ git lga
* 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
  1. Després, realitzarem la fusió de la branca verdura a la branca main, on es produirà un conflicte.
jpuigcerver@fp:~/git_branques (main) $ git merge verdura
Auto-merging menjar.txt
CONFLICT (content): Merge conflict in menjar.txt
Automatic merge failed; fix conflicts and then commit the result.
jpuigcerver@fp:~/git_branques (main|MERGING) $ git status
On branch main
You 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.txt

no changes added to commit (use "git add" and/or "git commit -a")
jpuigcerver@fp:~/git_branques (main|MERGING) $ cat menjar.txt
Pa
Macarrons
<<<<<<< HEAD
Pomes
Bresquilles
=======
Tomaques
Creïlles
>>>>>>> verdura
  1. 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) $ code menjar.txt # (1)!
jpuigcerver@fp:~/git_branques (main|MERGING) $ git add menjar.txt
jpuigcerver@fp:~/git_branques (main|MERGING) $ git status
On branch main
All conflicts fixed but you are still merging.
  (use "git commit" to conclude merge)

Changes to be committed:
    modified:   menjar.txt

jpuigcerver@fp:~/git_branques (main|MERGING) $ git commit -m "Merge branch 'verdura' into 'main'"
[main ce43962] Merge branch 'verdura' into 'main'
jpuigcerver@fp:~/git_branques (main) $ git lga
*   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
  1. Editem el fitxer menjar.txt per eliminar les marques de conflicte i mantenir els canvis de les dues branques.

Canvi de base (rebase)

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.

Aquest procés es realitza amb l'ordre:

git rebase <branca>

Documentació

Història abans del canvi de base

Figura 12. Història abans del canvi de base.

Història després del canvi de base

Figura 13. Història després del canvi de base.

Important

L'ordre git rebase canviarà la base de la branca actual (HEAD).

Preparació branques divergents desdejuni i paella
  1. Creem les branques desdejuni i paella des de la branca main.
jpuigcerver@fp:~/git_branques (main) $ git checkout main
Already on 'main'
jpuigcerver@fp:~/git_branques (main) $ git branch desdejuni
jpuigcerver@fp:~/git_branques (main) $ git branch paella
jpuigcerver@fp:~/git_branques (main) $ git lga -3 # (1)!
*   ce43962 - (0 seconds ago) Merge branch 'verdura' into 'main' - Joan Puigcerver (HEAD -> main, paella, desdejuni)
|\  
| * e4cfd4d - (0 seconds ago) Verdura - Joan Puigcerver (verdura)
* | d32e3ba - (1 second ago) Fruta - Joan Puigcerver (fruta)
|/  
  1. Afegim un producte al fitxer beguda.txt en la branca desdejuni.
jpuigcerver@fp:~/git_branques (main) $ git checkout desdejuni
Switched to branch 'desdejuni'
jpuigcerver@fp:~/git_branques (desdejuni) $ echo "Café" >> beguda.txt
jpuigcerver@fp:~/git_branques (desdejuni) $ git add beguda.txt
jpuigcerver@fp:~/git_branques (desdejuni) $ git commit -m "Beguda desdejuni"
[desdejuni 8a3c34b] Beguda desdejuni
 1 file changed, 1 insertion(+)
jpuigcerver@fp:~/git_branques (desdejuni) $ git lga -3
* 8a3c34b - (0 seconds ago) Beguda desdejuni - Joan Puigcerver (HEAD -> desdejuni)
*   ce43962 - (0 seconds ago) Merge branch 'verdura' into 'main' - Joan Puigcerver (paella, main)
|\  
| * e4cfd4d - (0 seconds ago) Verdura - Joan Puigcerver (verdura)
  1. Afegim alguns producte al fitxer menjar.txt en la branca paella.
jpuigcerver@fp:~/git_branques (desdejuni) $ git checkout paella
Switched to branch 'paella'
jpuigcerver@fp:~/git_branques (paella) $ echo "Arròs" >> menjar.txt
jpuigcerver@fp:~/git_branques (paella) $ echo "Pollastre" >> menjar.txt
jpuigcerver@fp:~/git_branques (paella) $ git add menjar.txt
jpuigcerver@fp:~/git_branques (paella) $ git commit -m "Menjar paella"
[paella 641c594] Menjar paella
 1 file changed, 2 insertions(+)
jpuigcerver@fp:~/git_branques (paella) $ git lga -3
* 8a3c34b - (0 seconds ago) Beguda desdejuni - Joan Puigcerver (desdejuni)
| * 641c594 - (0 seconds ago) Menjar paella - Joan Puigcerver (HEAD -> paella)
|/  
*   ce43962 - (0 seconds ago) Merge branch 'verdura' into 'main' - Joan Puigcerver (main)
|\  
  1. L'opció git log -3 permet consultar els últims tres commit de la història del repositori.
Exemple: Fusió de les branques desdejuni i paella amb canvi de base

Anem a fusionar les branques desdejuni i paella a la branca main mantenint una història lineal.

  1. Fusionem la branca desdejuni a la branca main amb una fusió directa (fast-forward).
jpuigcerver@fp:~/git_branques (paella) $ git checkout main
Switched to branch 'main'
jpuigcerver@fp:~/git_branques (main) $ git merge desdejuni
Updating ce43962..8a3c34b
Fast-forward
 beguda.txt | 1 +
 1 file changed, 1 insertion(+)
jpuigcerver@fp:~/git_branques (main) $ git lga -3
* 8a3c34b - (0 seconds ago) Beguda desdejuni - Joan Puigcerver (HEAD -> main, desdejuni)
| * 641c594 - (0 seconds ago) Menjar paella - Joan Puigcerver (paella)
|/  
*   ce43962 - (0 seconds ago) Merge branch 'verdura' into 'main' - Joan Puigcerver
|\  
  1. La branca paella 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.
jpuigcerver@fp:~/git_branques (main) $ git checkout paella
Switched to branch 'paella'
jpuigcerver@fp:~/git_branques (paella) $ git rebase main
Rebasing (1/1)
Successfully rebased and updated refs/heads/paella.
jpuigcerver@fp:~/git_branques (paella) $ git lga -3
* 8c43498 - (0 seconds ago) Menjar paella - Joan Puigcerver (HEAD -> paella)
* 8a3c34b - (0 seconds ago) Beguda desdejuni - Joan Puigcerver (main, desdejuni)
*   ce43962 - (0 seconds ago) Merge branch 'verdura' into 'main' - Joan Puigcerver
|\  
  1. Després del canvi de base, es possible fusionar la branca paella a la branca main amb una fusió directa (fast-forward).
jpuigcerver@fp:~/git_branques (paella) $ git checkout main
Switched to branch 'main'
jpuigcerver@fp:~/git_branques (main) $ git merge paella
Updating 8a3c34b..8c43498
Fast-forward
 menjar.txt | 2 ++
 1 file changed, 2 insertions(+)
jpuigcerver@fp:~/git_branques (main) $ git lga -3
* 8c43498 - (0 seconds ago) Menjar paella - Joan Puigcerver (HEAD -> main, paella)
* 8a3c34b - (0 seconds ago) Beguda desdejuni - Joan Puigcerver (desdejuni)
*   ce43962 - (0 seconds ago) Merge branch 'verdura' into 'main' - Joan Puigcerver
|\  

Resolució de conflictes

El canvi de base (rebase) també pot provocar conflictes si les dues branques han modificat la mateixa part d'un fitxer.

Git ens indicarà que hi ha conflictes i caldrà resoldre'ls manualment, d'una manera similar a la resolució de conflictes en la fusió de branques.

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
  1. Creem les branques postre i aperitiu des de la branca main.
jpuigcerver@fp:~/git_branques (main) $ git checkout main
Already on 'main'
jpuigcerver@fp:~/git_branques (main) $ git branch postre
jpuigcerver@fp:~/git_branques (main) $ git branch aperitiu
jpuigcerver@fp:~/git_branques (main) $ git lga -3
* 8c43498 - (0 seconds ago) Menjar paella - Joan Puigcerver (HEAD -> main, postre, paella, aperitiu)
* 8a3c34b - (0 seconds ago) Beguda desdejuni - Joan Puigcerver (desdejuni)
*   ce43962 - (0 seconds ago) Merge branch 'verdura' into 'main' - Joan Puigcerver
|\  
  1. Afegim un producte al fitxer menjar.txt en la branca postre.
jpuigcerver@fp:~/git_branques (main) $ git checkout postre
Switched to branch 'postre'
jpuigcerver@fp:~/git_branques (postre) $ echo "Iogurt" >> menjar.txt
jpuigcerver@fp:~/git_branques (postre) $ git add menjar.txt
jpuigcerver@fp:~/git_branques (postre) $ git commit -m "Menjar postre"
[postre 0d71e2b] Menjar postre
 1 file changed, 1 insertion(+)
jpuigcerver@fp:~/git_branques (postre) $ git lga -3
* 0d71e2b - (0 seconds ago) Menjar postre - Joan Puigcerver (HEAD -> postre)
* 8c43498 - (0 seconds ago) Menjar paella - Joan Puigcerver (paella, main, aperitiu)
* 8a3c34b - (0 seconds ago) Beguda desdejuni - Joan Puigcerver (desdejuni)
  1. Afegim alguns producte al fitxer menjar.txt en la branca aperitiu.
jpuigcerver@fp:~/git_branques (postre) $ git checkout aperitiu
Switched to branch 'aperitiu'
jpuigcerver@fp:~/git_branques (aperitiu) $ echo "Olives" >> menjar.txt
jpuigcerver@fp:~/git_branques (aperitiu) $ git add menjar.txt
jpuigcerver@fp:~/git_branques (aperitiu) $ git commit -m "Menjar aperitiu"
[aperitiu f3c59a0] Menjar aperitiu
 1 file changed, 1 insertion(+)
jpuigcerver@fp:~/git_branques (aperitiu) $ git lga -3
* f3c59a0 - (0 seconds ago) Menjar aperitiu - Joan Puigcerver (HEAD -> aperitiu)
| * 0d71e2b - (0 seconds ago) Menjar postre - Joan Puigcerver (postre)
|/  
* 8c43498 - (0 seconds ago) Menjar paella - Joan Puigcerver (paella, 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.

jpuigcerver@fp:~/git_branques (aperitiu) $ git checkout main
Switched to branch 'main'
jpuigcerver@fp:~/git_branques (main) $ git lga -3
* f3c59a0 - (0 seconds ago) Menjar aperitiu - Joan Puigcerver (aperitiu)
| * 0d71e2b - (0 seconds ago) Menjar postre - Joan Puigcerver (postre)
|/  
* 8c43498 - (0 seconds ago) Menjar paella - Joan Puigcerver (HEAD -> main, paella)
jpuigcerver@fp:~/git_branques (main) $ git show postre
commit 0d71e2b271221eaffbd345ef63010188981a973a
Author: Joan Puigcerver <j.puigcerveribanez@edu.gva.es>
Date:   Tue Oct 29 16:24:00 2024 +0100

    Menjar postre

diff --git a/menjar.txt b/menjar.txt
index 1a238ca..aef7ce7 100644
--- a/menjar.txt
+++ b/menjar.txt
@@ -6,3 +6,4 @@ Tomaques
 Creïlles
 Arròs
 Pollastre
+Iogurt
jpuigcerver@fp:~/git_branques (main) $ git show aperitiu
commit f3c59a0b667fa226aa7b6c8918d7ed27c377bbc9
Author: Joan Puigcerver <j.puigcerveribanez@edu.gva.es>
Date:   Tue Oct 29 16:24:00 2024 +0100

    Menjar aperitiu

diff --git a/menjar.txt b/menjar.txt
index 1a238ca..c6c2b2b 100644
--- a/menjar.txt
+++ b/menjar.txt
@@ -6,3 +6,4 @@ Tomaques
 Creïlles
 Arròs
 Pollastre
+Olives
  1. Primer, realitzem una Fusió directa entre de la branca postre a la branca main, on no hi haurà conflictes.
jpuigcerver@fp:~/git_branques (main) $ git checkout main
Already on 'main'
jpuigcerver@fp:~/git_branques (main) $ git merge postre
Updating 8c43498..0d71e2b
Fast-forward
 menjar.txt | 1 +
 1 file changed, 1 insertion(+)
jpuigcerver@fp:~/git_branques (main) $ git lga -3
* f3c59a0 - (0 seconds ago) Menjar aperitiu - Joan Puigcerver (aperitiu)
| * 0d71e2b - (0 seconds ago) Menjar postre - Joan Puigcerver (HEAD -> main, postre)
|/  
* 8c43498 - (0 seconds ago) Menjar paella - Joan Puigcerver (paella)
  1. 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) $ git checkout aperitiu
Switched to branch 'aperitiu'
jpuigcerver@fp:~/git_branques (aperitiu) $ git rebase main
Auto-merging menjar.txt
CONFLICT (content): Merge conflict in menjar.txt
Rebasing (1/1)
error: could not apply f3c59a0... Menjar aperitiu
hint: Resolve all conflicts manually, mark them as resolved with
hint: "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 aperitiu
jpuigcerver@fp:~/git_branques (aperitiu|REBASE 1/1) $ git status
interactive rebase in progress; onto 0d71e2b
Last command done (1 command done):
   pick f3c59a0 Menjar aperitiu
No 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.txt

no changes added to commit (use "git add" and/or "git commit -a")
  1. 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) $ code menjar.txt # (1)!
jpuigcerver@fp:~/git_branques (aperitiu|REBASE 1/1) $ git add menjar.txt
jpuigcerver@fp:~/git_branques (aperitiu|REBASE 1/1) $ git status
interactive rebase in progress; onto 0d71e2b
Last command done (1 command done):
   pick f3c59a0 Menjar aperitiu
No 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.txt

jpuigcerver@fp:~/git_branques (aperitiu|REBASE 1/1) $ git rebase --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) $ git lga -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)
  1. Després del canvi de base, es possible fusionar la branca apeririu a la branca main amb una fusió directa (fast-forward).
jpuigcerver@fp:~/git_branques (aperitiu) $ git checkout main
Switched to branch 'main'
jpuigcerver@fp:~/git_branques (main) $ git merge aperitiu
Updating 0d71e2b..0a53b4b
Fast-forward
 menjar.txt | 1 +
 1 file changed, 1 insertion(+)
jpuigcerver@fp:~/git_branques (main) $ git lga -3
* 0a53b4b - (0 seconds ago) Menjar aperitiu - Joan Puigcerver (HEAD -> main, aperitiu)
* 0d71e2b - (0 seconds ago) Menjar postre - Joan Puigcerver (postre)
* 8c43498 - (0 seconds ago) Menjar paella - Joan Puigcerver (paella)
  1. Hem eliminat les marques de conflicte i hem combinat els dos textos.

Recursos addicionals

Bibliografia

Comentaris