Tutorial de git/Treball nolineal

A partir d'aquest punt trobarem les funcions que fan més interessant el git (a part del treball amb les branques). Definim el treball nolineal com aquell en que, pels motius que siguin, tenim oberts diferents fronts en el nostre projecte que no són en paraŀlel, i que fan créixer el valor d'un control de versions com el git.

Ara et deus estar dient que mai no et trobaràs en una situació com aquesta, però la veritat és que és més habitual del que pot semblar. No t'has trobat mai en que has donat una versió del projecte al teu cap o client, i mentre ell se la mirava tu has estat afegint noves funcionalitats al teu projecte i de cop el client t'ha demanat que de la versió que li has passat vol afegir una cosa i treballar amb aquella versió mentre tu afegeixes les noves funcionalitats?

Això no és una situació tan estranya i si no tens un control de versions es fa difícil de resoldre. De cop passes a tenir 2 versions molt diferents del projecte i per ajuntar-les necessites un munt de paciència (com a mínim!)

Moure't per la història del repositori[modifica]

El primer que necessitem conèixer per a poder treballar amb depèn de quina situació és com moure'ns entre els diferents commits. Això és molt necessari per exemple quan el teu cap et demana que facis uns canvis concrets a una versió del projecte.

La comanda per canviar de commit és una vella coneguda: git checkout. Tot i que en aquest cas en lloc de dir-li una branca li direm un commit o una etiqueta.

En el cas que hàgim posat una etiqueta concreta al punt concret, arribar-hi és tant fàcil com fer: git checkout 0.1. Amb aquesta comanda ja som en el punt de la història que volíem.

Si no hi hem posat cap etiqueta el que farem servir és qualsevol de les maneres que coneixem de referenciar un commit explicats a #Mirar la història del repositori. El més comú és fer servir la ID del commit. Per exemple podríem fer: git checkout 88b37174
Una altra manera és fer servir la titlla. Per anar a l'antepenúltim commit tot el que hem de fer és git checkout HEAD~3

Un cop hem fet el checkout el git ja ens diu que ens estem movent a un punt que no és una branca local. Si correm la comanda git branch podem veure que ens diu que no estem a cap branca. Això ens permet crear un fitxer comprimit amb els continguts, o compilar un binari, o obtenir un pdf. Un cop hem acabat podem tornar a l'últim commit de la branca fent un git checkout branca igual com si volguéssim canviar de branca (recordem que ara no estem a cap branca)

Si el que volem és modificar el codi d'aquell commit aquest camí no ens ho permet, ja que en no estar en una branca no podem fer cap commit. Per tant si volem modificar fitxers no podem, ja que es perdran. La solució per poder modificar és crear una branca des d'un punt de la història.

Crear una branca des del passat[modifica]

Si al cap d'un quant temps productiu de treball el nostre cap (o client) ens diu que vol afegir una funcionalitat a una versió anterior del projecte, el que hem de fer és crear una branca des d'aquell punt per tal de poder modificar el codi.

Molts de vosaltres potser ja esteu pensant en la manera. Deveu pensar: "Això és fàcil, ens movem al punt de la història i des d'allà creem una branca". Doncs sí, això és correcte. Podeu fer-ho d'aquesta manera, és a dir: git checkout 0.1; git branch nova_funcionalitat_0.1. Això funciona, però si recordem quan parlàvem de com crear branques, en crear una branca no ens movem a ella. Per tant encara ens faltaria fer un git checkout nova_funcionalitat_0.1.

Això en total són 3 comandes per tal de crear una branca des del passat. Com ja deveu pensar el git té una manera més còmode de fer-ho. Si recordem quan parlàvem de com crear una branca ja us he donat una solució. En aquest cas li donem una mica més d'informació. Així doncs per fer la branca des de l'etiqueta 0.1 com hem volgut fer abans la comanda que ens va bé és: git checkout 0.1 -b nova_branca

Aquesta drecera també ens serveix per quan volem començar una branca des d'una branca en la que no estem. En lloc de primer anar a la branca i després crear la branca, simplement podem fer git checkout branca_on_no_estem -b nova_branca

Aplaçar canvis temporalment[modifica]

Moltes vegades quan el teu cap et demana "per ahir" que facis un canvi en una versió anterior del projecte tu estàs a mig fer d'alguna cosa complicada i no és el moment de fer el commit. Tal com ja deveu haver vist en aquest punt no ets capaç de canviar de branca ni res ja que no tens l'àrea de treball neta. A partir d'aquí tens dues opcions, o acabes el que estaves fent (si és petit cap problema, però si és gran allò que havia de ser "per ahir" no serà ni per la setmana que ve), o bé aplaçar temporalment els canvis que estàs fent.

Aquesta última alternativa és la que discutim en aquí. La comanda que t'aplaça els canvis temporalment és el git stash. Bàsicament el que fa és guardar els canvis que has fet i et torna al mateix estat que després de l'últim commit. D'aquesta manera pots canviar de branca i fer la feina tant important!

Un cop has acabat el que t'havien demanat, pots tornar a la branca on eres i amb un git stash apply tornes a tenir l'àrea de treball tal com la tenies abans de la trucada del cap.

Aquesta eina va molt bé però és molt adictiva, i moltes vegades has aplaçat moltes coses. Per això amb la comanda git stash list ets capaç de veure totes les coses que tens aplaçades. Amb un git stash show stash@{1} pots veure els detalls del stash (el 1 en aquest cas); i evidentment pots tornar a posar els canvis de qualsevol stash amb un git stash apply stash@{1}

També podem afegir-li un missatge al stash amb l'ordre git stash save "missatge pel stash"

Canviar el punt de sortida d'una branca[modifica]

El tema del que parlaré en aquest apartat no s'aprecia fins que no et trobes en situació. És una mica poc intuïtiu, però és molt útil. Per tal de fer-ho més entenedor us parlaré dels dos casos en què jo he trobat més interessant de fer-lo anar.

Primer de tot intentaré explicar què significa això de canviar el punt de sortida d'una branca. Imaginem que tenim una branca on estem fent una prova. Aquesta branca la vam crear des de l'etiqueta 0.1 (git checkout 0.1 -b nova_branca). Mentre fem la prova la branca principal arriba a una altra versió del programa i s'etiqueta amb el 0.2. Això vol dir que el programa de la versió 0.2 és diferent que el de la 0.1, però a la branca on estàs fent proves aquests canvis no hi són.

Ara et deus preguntar perquè no ens coŀloquem a la branca i simplement fem un git merge master. Això realment seria una solució, però realment el que volem fer és afegir la branca master a la de proves? Si ho fem així en un punt del nostre historial sortirà que hem ajuntat les dos branques. Per què ho hem fet?

La comanda git rebase ens dona una altra solució per aquesta situació. El que fa és agafar la branca de proves i posar-la com si l'haguéssim creat en el punt de l'etiqueta 0.2. Això és més elegant i és el que estem fent... Incorporem una funció a partir de la versió 0.2. La sintaxi de l'ordre seria git rebase 0.2 nova_branca. Això vol dir que el punt 0.2 serà el punt de sortida de la branca nova_branca.

De les dues situacions en que faig servir més el rebase la més clara és la primera. I de fet és molt semblant a la que acabo d'explicar (per no dir la mateixa). Quan estic treballant en més d'una branca i n'acabo una, el que faig és que la acabada la passo a la branca principal mitjançant un merge. Llavors totes les branques que crec que poden estar afectades pels canvis els faig un rebase al punt on he fet el merge.
Això també serveix per quan es treballa amb més gent, i la resta de la gent fa commits a la branca principal, és un bon moment per fer un rebase i veure si els seus canvis afecten els teus.

El segon cas és una mica més rarot... Imaginem que (tal com hem dit força vegades durant aquesta secció) el nostre cap (o client) ens demana que li afegeixis una certa funcionalitat a una versió determinada. Nosaltres creem la branca i afegim la funció. En aquest moment ens troben que la funció que acabem de afegir només està a la branca que acabem de crear i no està en la nostra branca principal. En aquest moment podem fer dos coses. O ajuntem la nova branca a la branca principal (tenim els mateixos dubtes que abans) o el que fem és que canviar el punt de sortida de la nostra branca principal i que en lloc de sortir del 0.1 surti de la nova branca. Per tal de fer això l'ordre serà: git rebase nova_branca master.

En aquest punt ens podem trobar que ens surtin conflictes, i el git ens ho avisarà. Tal com hem explicat en l'apartat #Resoldre conflictes podem resoldre els conflictes a ma, o fer servir un programa amb el git mergetool. Un cop ho hem solucionat ja podem fer que acabi de fer el rebase amb un git rebase --continue.

Si ara mirem la història (git log) podem veure com a partir de la etiqueta 0.1 ens trobem amb els canvis de la nova_branca i a continuació amb la resta de la branca principal tal com la teníem.

Una recomanació és que abans (o després, no és important) de fer el rebase, quan ja hem acabat la nova funcionalitat etiquetem la branca ex: 0.1.1 D'aquesta manera sabem que en aquest punt tenim una nova_funcionalitat a partir del punt 0.1. I un cop ja hem fet el rebase podem esborrar la branca amb la nova funcionalitat git branch -d nova_branca

Dominant tot el que hem parlat fins ara ja pots treballar amb git de manera autònoma d'una manera molt profitosa. A partir d'aquí veurem com compartir el teu repositori amb la resta del món i com agafar els repositoris de la resta del món i aportar-hi coses.