Distributed SCM, 2. dio - osnove rada sa git-om

U prošlom postu pokušao sam objasniti zašto je korištenje nekog DVCSa dobra ideja, čak i ako jedini radite na projektu, čak i ako se ne bavite programiranjem.
Kako to izgleda u praksi, vidjet ćemo uz primjere korištenja git sustava. Git je vjerojatno najpopularniji DVCS danas - koriste ga Linux kernel (za kojeg je i napisan), X.Org, GNOME i mnogi drugi projekti. Osim git-a, popularni DVCSovi danas su mercurial (hg) (Python, Mozilla, OpenSolaris) i bazaar (bzr) (Ubuntu). Iako se pojedini pojmovi i način rada pomalo razlikuju od sustava do sustava, osnovna ideja je ista.
Za primjer projekta uzet ćemo Hello World program u C-u pod Linuxom. Osim samog programa (hello.c datoteka), projekt će sadržavati i Makefile datoteku za buildanje programa te README.txt sa uputama za program.
Inicijalizacija repozitorija
Kako tek započinjemo s projektom, kreirat ćemo novi prazni direktorij hello i u njemu inicijalizirati prazan git repozitorij (u mom primjeru, projektni direktorij kreirao sam u home (~) direktoriju):
> mkdir hello > cd hello > git init Initialized empty Git repository in ~/hello/.git/
Git sve svoje podatke sprema u poddirektorij .git vašeg projekta. Za razliku od SVN-a, koristi se samo jedan za cijeli projekt, a ne po jedan za svaki direktorij unutar vašeg projekta.
Ukoliko ste već prije započeli rad na projektu, a želite početi koristiti git na njemu, možete pokrenuti git init izravno u direktoriju projekta. Ukoliko želite raditi sa repozitorijem nekog projekta koji već koristi git, originalni repozitorij možete klonirati i onda raditi na svom klonu. Primjer za Empathy (GNOME chat client):
> cd /tmp > git clone git://git.collabora.co.uk/git/empathy.git Initialized empty Git repository in /tmp/empathy/.git/ ....
Committanje
Jednom kad ste inicijalizirali projekt (na bilo koji način), možete raditi promjene i spremati ih. Kreirajmo datoteku hello.c i u nju spremimo prvu verziju našeg Hello World programa:
void main() { printf("Hello world\n"); }
Brza provjera pokazuje da se stvar kompajlira uz neka upozorenja. Za početak smo zadovoljni pa želimo spremiti prvu verziju. Da bi to napravili, gitu moramo reći da mora voditi računa o datoteci hello.c te da smo je upravo promijenili:
> git add hello.c
Napomena: ovaj add ne znači da smo datoteku dodali u repozitorij, nego smo datoteku označili za spremanje u slijedećem commitu. Pošto datoteka još nije bila spremana u repozitoriju, prilikom commita će biti stvorena. Napravimo commit sa komentarom “prva verzija” (ukoliko ne dodate komentar u komandnoj liniji, otvorit će vam se editor pa se možete raspisati do mile volje):
> git commit -m "prva verzija" [master (root-commit) ac3a485] prva verzija 1 files changed, 2 insertions(+), 0 deletions(-) create mode 100644 hello.c
Git nam govori da je spremio prvi commit u master granu (branch). Master grana uvijek postoji u git repozitoriju (osim ako radite čudne stvari :) te se automatski stvara kod inicijalizacije novog repozitorija ili kloniranja postojećeg, te je po defaultu aktivna.
Pogledajmo što zasad sve imamo spremljeno u master grani:
> git log commit ac3a48559de3b7f225ffa96c504a8586057fb4b9 Author: Senko Rasic <senko@localhost> Date: Fri Dec 11 00:38:16 2009 +0100 prva verzija
Ovaj čudan niz znakova u prvoj liniji je jedinstveni ID commita. Kada želite pregledavati pojedine committove, vraćati se natrag u prošlost, preuzimati committove iz jedne grane u drugi i slično, obično trebate koristiti ovaj ID. Ne brinite, ne trebate ga ručno utipkavati cijelog - git je dovoljno pametan da ga prepozna iz prvih par znakova (bar 4, ali ukoliko ima više committova koji počinju sa istim znakovima, onda možete specificirati i više znakova). Pogledajmo detalje ovog što smo upravo spremili:
> git show ac3a
commit ac3a48559de3b7f225ffa96c504a8586057fb4b9
Author: Senko Rasic <senko@localhost>
Date: Fri Dec 11 00:38:16 2009 +0100
prva verzija
diff --git a/hello.c b/hello.c
new file mode 100644
index 0000000..d08059a
--- /dev/null
+++ b/hello.c
@@ -0,0 +1,2 @@
+void main() { printf("Hello world\n"); }
+
Commit se sastoji od jedinstvenog ID-a, različitih polja (najčešća su Author i Date, ali može ih biti još), poruke koja opisuje što se napravilo (može biti jedan ili više redova), te same promjene u diff formatu. U ovom slučaju kreirali smo novu datoteku pa u diffu piše da je prethodna verzija bila /dev/null :)
Da biste pogledali zadnji commit, ne morate navoditi njegov ID. Tako smo istu stvar mogli napraviti i samo pozivanjem git show.
Work tree, staging area
Krenimo dalje sa našim projektom. Prva verzija datoteke nije baš reprezentativni C kod, pa ćemo je malo uljepšati. Editirajmo hello.c da izgleda ovako:
void main(void)
{
printf ("Hello world\n");
}
U ovom trenutku u našem radnom direktoriju (work tree) postoje promjene koje još nismo pospremili. Nepospremljene promjene možemo vidjeti u diff formatu:
> git diff
diff --git a/hello.c b/hello.c
index d08059a..145804e 100644
--- a/hello.c
+++ b/hello.c
@@ -1,2 +1,4 @@
-void main() { printf("Hello world\n"); }
-
+void main(void)
+{
+ printf ("Hello world\n");
+}
Ukoliko je promjenjeno više datoteka, razlike za pojedinu datoteku (ili pojedini direktorij) možemo vidjeti navodeći njeno ime: git diff hello.c. Primjetite da će git diff pokazati promjene samo u datotekama koje prati, odnosno one za koje ste mu rekli da su dio repozitorija. Čim datoteku jednom označite sa git add, git će je pratiti (sve dok je ne obrišete sa git rm). Pregled stanja radnog direktorija, s popisom promjenjenih praćenih(tracked) i popisom nepraćenih (untracked) datoteka možemo vidjeti sa git status.
Označimo datoteku za spremanje:
> git add hello.c
> git diff
> git diff --cached
diff --git a/hello.c b/hello.c
index d08059a..145804e 100644
--- a/hello.c
+++ b/hello.c
@@ -1,2 +1,4 @@
-void main() { printf("Hello world\n"); }
-
+void main(void)
+{
+ printf ("Hello world\n");
+}
Nakon označivanja za spremanje, git diff više ne prijavljuje nikakve razlike! To je zbog toga što su razlike sada u pripremi za commit (staging area). Pripremljene razlike možemo vidjeti sa git diff --cached. Ukoliko smo nešto zeznuli pa ne želimo napraviti commit nad njima, git reset miče promjene nazad iz staging area i možemo ih nakadno opet označiti za spremanje. Napomena: git reset, ukoliko dodate još neke parametre (npr. –hard koji briše promjene i iz radnog direktorija, ili id committa, koji vas “vraća” na taj commit i briše sve naknadne promjene), može biti vrlo opasna po vaše podatke. Prilikom eksperimentiranja svakako pročitajte manual za git-reset.
Ukoliko samo želite označiti sve promjenjene datoteke (koje su dodane u repozitorij, tj. git ih prati) i napraviti commit svih njih, to možete učiniti ovako:
> git commit -a -m "malo uljepsano" git commit -a -m "malo uljepsano" [master 459b8bd] malo uljepsano 1 files changed, 4 insertions(+), 2 deletions(-)
Uz ove osnove možete već početi raditi sa repozitorijom, spremati i pregledavati svoje promjene, ali zasad se git ne čini previše različitim od CVS-a ili SVN-a. U slijedećem postu bacit ćemo se na zanimljivije stvari - branching, merging, rebasing. Stay tuned :)


