Git branching and merging

Oggi mostreremo alcuni esempi veramente basilari di come usare il prompt di git per creare dei branch e fare dei merge.

Come sapete il Pro Git Book, scritto da Scott Chacon, è interamente leggibile online qui

Vi siete mai chiesti qual è la storia di Git? Qui un breve excursus. Ma quello ancora più interessante è che Git prende il nome sempre da Linus Torvalds. Dopo aver dato il proprio nome ad uno dei sistemi operativi più diffusi, ha voluto descrivere nel progetto di Version Control anche un’aspetto della sua personalità. Da qui nasce il termine Git che in slang sta a significare una persona “poco simpatica” 🙂 (Wikipedia, Urban Dictionary).

Bene dopo un po’ di storia, sempre divertente (molto spesso le persone utilizzano gli strumenti ma non si chiedono il perché del loro nome o da dove sono “arrivati”) passiamo alla pratica.

Bene creiamo la cartella “git_merge” e inizializziamo in essa un repository Git.

Creiamo quindi il file “hello.py” seguente:

#! /usr/bin/env python

def hello():
    print "Hello World!\n"

hello()

lo rendiamo eseguibile

chmod +x hello.py

e lo aggiungiamo al repository

git add hello.py
git commit -m 'added hello.py'

A questo punto creiamo un “branch” e lo chiamiamo “refactoring” e ci spostiamo in esso.

git branch refactoring
git checkout refactoring

e modifichiamo il file cambiamo il nome della funzione hello() in say_hello() e committiamo.

A questo punto decidiamo che il rename della funzione ci piace di più e vogliamo quindi fare il merge di ‘refactoring’ nel ‘master’.

git checkout master
git merge refactoring

In questo caso il merge è semplice e viene fatto il cosiddetto fast-forward. Ovvero master viene portato avanti nel nodo di refactoring. In parole povere il merge è automatico.

Visualizzando il grafico del log, non si vedono “rami” in quanto sia ‘master’ che ‘refactoring’ puntano allo stesso ‘nodo’ nella struttura dei commit.

Adesso, modifichiamo il file hello.py nel master aggiungendo il commento alla funzione say_hello()

Poi passiamo al branch refactoring in cui invece modifichiamo la funzione say_hello() in greetings() e aggiungiamo anche un parametro in ingresso.

A questo punto decidiamo di voler mettere il parametro in ingresso anche in master e quindi facciamo un merge.

git checkout master
git merge refactoring

E ta-dan! Git questa volta non riesce in automatico a risolvere i conflitti perché non sa quale sia la modifica che si vuole tenere e quale quella da eliminare.

Il file hello,py viene modificato nel seguente modo:

Cosa vediamo? 🙂

Una cosa che ci fa paura… allora digitiamo

git merge --abort

E tutto tornerà come prima.

Se invece vogliamo procedere al merge allora dobbiamo conoscere i seguenti simboli

<<<<<<<<

Da questo simbolo fino al successivo, vediamo il file presente nella HEAD del repository su cui siamo, in questo caso master. Abbiamo infatti il nostro commento alla funzione.

|||||||||

Da questo simbolo al successivo vi è la precedente versione del file (quella della commit precedente) rispetto al repository in cui ci troviamo, quindi master.

Quindi se guardiamo questa parte centrale e la parte in alto avremo le differenze tra il file corrente nel repository master e quello della commit precedente.

========

Da questo simbolo fino al successivo (ultimo) abbiamo il file del branch da cui vogliamo fare il merge, in questo caso: refactoring.

Quindi se guardiamo la parte centrale e la parte in basso avremo le differenze tra il file corrente nel branch e quello della commit precedente nel master.

>>>>>>>>

Tutto quello che sta fuori, è quello che Git ha deciso di tenere e non sta considerando come un conflitto.

 

Quindi… se vogliamo tenere il file nel master basta tenere la parte tra <<<<<< e |||||| e cancellare tutto il resto compresi questi simboli.

Se vogliamo tenere il file del branch refactoring basta tenere la parte tra ====== e >>>>>> e cancellare tutto il resto compresi questi simboli.

Se invece, come faremo, vogliamo tenere un po’ di una parte e un po’ dell’altra, andremo ad integrare la parte centrale con le aggiunte (o riscrivete il file, come vi piace di più) della parte sopra e della parte sotto.

Ecco alla fine come abbiamo modificato il file.

#! /usr/bin/env python

def greetings(who):
    """ Say hello to the World! """
    print "Hello, " + who

greetings("Fabio")

A questo punto facciamo la commit.

Fine 🙂

Da qui in poi invece leggete solo se siete curiosi.

Se quando abbiamo avuto dei conflitti avessimo voluto isolare le tre sezioni in file differenti per poi farne i diff tra di loro, avremmo potuto eseguire i seguenti comandi:

git show :1:hello.py > hello.base.py
git show :2:hello.py > hello.head.py
git show :3:hello.py > hello.refactoring.py

A questo punto se volete fare il diff tra di loro si può fare per esempio:

git diff --no-index hello.base.py hello.refactorug.py

E una volta terminata la vostra correzione dei conflitti nel file hello.py potete pulire tutto con

git clean -f

Okay, okay… voi utilizzate il tool grafico, ma… sapere come funziona sotto non è bello?! 😀

Git branching and merging

Git bash diff explained

In uno dei nostri articoli precedenti su Git abbiamo spiegato come avere una bash Git configurata secondo alcuni nostri ( miei 😛 ) gusti.

Anche oggi, volendo essere un po’ geek, andremo a sviscerare con attenzione come fare il diff con Git attraverso la linea di comando 🙂

Dai… è divertente! 🙂

Per prima cosa creiamo una cartella di prova sul nostro desktop e creiamoci un repository git:

Poi inseriamo un file (prendiamo l’html di example.org) e lo committiamo.

a questo punto lo apriamo con il nostro editor di testo preferito e ne facciamo alcune modifiche.

Quindi lanciamo il diff per quel file. Ecco che cosa verrà visualizzato (più o meno in base alle vostre modifiche)

Compared Files

Diff ci dice che sta comparando il file “a” che è example.org.txt con il file “b” che è sempre, in questo caso il file example.org.txt ma con le modifiche

File Metadata

Queste sono informazioni molto tecniche che dipendono da come Git identifica gli “oggetti” al suo interno (potete non considerarli per lo scopo di questo articolo)

a/b Identifiers 

Git da degli identificatori per le righe che visualizzerà successivamente per indicare una riga di “a” e una riga di “b”. Qui si vede che le righe di “a” saranno indicate con il simbolo “-” (meno) mentre quelle di “b” con il simbolo “+” (più).

Poiché il diff di un file con la sua versione precedente identifica con  “a” la versione precedente e con “b”  la versione successiva, in questo modo è anche possibile leggere le righe con “-” come eliminate mentre quelle con “+” come aggiunte. Infatti le righe identiche non vengono etichettate.

Chunk Header

Poiché le modifiche ad un file potrebbero espandersi per molte righe di codice, diff cerca di visualizzare solamente degli “spezzoni” (chunk) dove sono presenti le modifiche, tralasciando le molte (o poche) righe identiche tra un chunk e l’altro.

L’header è racchiuso tra i tag @@

All’interno c’è una sezione per il file “a” indicata con il “-” e una per il file “b” indicata con il “+”.

Qui ci viene detto che nel chunk che segue sono visualizzate 7 righe di “a” a partire dalla riga 11 e 7 righe di b a partire dalla riga 11 se abbiamo (come il nostro primo chunk)

@@ -11,7 +11,7@@

Chunk

Nel chunk sono indicate con “-” le righe di “a” eliminate e con “+” le righe di “b” aggiunte. Le righe senza segni sono le stesse in entrambi i file

Git bash diff explained

Git bash environment on Mac OSX

Oggi vogliamo essere un po’ geek e configurarci il prompt per git a linea di comando.

Per fare questo scarichiamoci questi cinque file più, se volete utilizzarlo come editore predefinito, Sublime Text:

Il profilo dobbiamo importarlo dal menu del nostro terminale: Terminal –> Settings (io l’ho in inglese) e poi sotto la lista di profili, clicchiamo la piccola ruota dentata vicino al “-” (meno)

Scarichiamo (nella cartella Downloads)  bash_profile e apriamo il terminale e digitiamo:

cd
mv Downloads/bash_profile .bash_profile

ATTENZIONE: copiate il .bash_profile solo se non ne avete già uno, altrimenti integrate il vostro con quanto scritto nel mio.

Scarichiamo (nella cartella Downloads) git-prompt.sh e git-completion.bash (presi direttamente dal repository ufficiale di git su GitHub – https://github.com/git/git) e git_configure_commands.sh

Digitiamo i comandi

cd
mv Downloads/git_configure_commands.sh .
mv Downloads/git-completion.bash .
mv Downloads/git-prompt.sh .

A questo punto lanciamo il comando git_configure_commands.sh

./git_configure_commands.sh

che imposterà il conflict style a diff3, metterà il push.default ad upstream e imposterà Sublime Text come l’editore di default per il messaggio di commit (fate attenzione qui che il percorso di Sublime Text sia anche da voi come nel file, altrimenti correggetelo)

A questo punto non vi resta che chiudere e riaprire il terminale e navigare in una cartella che sia un repository git.

Ecco qua!

 

Si lo so, l’interfaccia grafica ci vizia un po’, ma avere anche una riga di comando tutta personalizzata non è male 😉

Git bash environment on Mac OSX

Start versioning with GIT

1. Git Init

Fare la init nella cartella di progetto per avere il repository locare
git_local_init

 

NOTA: Inserire subito l’ignore file in modo da non committare tutti i file compilati!

Esempio: .gitignore

bin
Bin
obj
*.suo
*.pdb
Thumbs.db

2. Commit => Master

Fare la commit dei file nel master locale
git_commit_master

3. Bare repo

Creare un repository “bare” in remoto o in locale su un disco esterno (per esempio)

In locale di solito le cartelle “bare” hanno come suffisso il .git
git_bare_1

Fare la push dal master locale al repository “bare”

git_push_1
git_push_2
git_push_3

4. Start Coding, Committing and Pushing 😀

Start versioning with GIT