Git
Chapters ▾ 2nd Edition

2.2 Osnove Gita - Snimanje promena na repozitorijumu

Snimanje promena na repozitorijumu

Sada imate pošten Git repozitorijum i čekaut (tj. radnu kopiju) fajlova za taj projekat. Treba da napravite neke izmene i komitujete snimke tih izmena na vaš repozitorijum svaki put kada projekat dosegne stanje koje želite da zabeležite.

Upamtite da svaki fajl u vašem radnom direktorijumu može biti u jednom od dva stanja: praćen ili nepraćen (tracked ili untracked). Praćeni fajlovi su fajlovi koji su bili u poslednjem snimku; oni mogu da budu razmodifikovani, modifikovani ili stejdžovani. Nepraćeni fajlovi su sve ostalo — bilo koji fajlovi u radnom direktorijumu koji nisu bili u poslednjem snimku i nisu na stejdžu. Kada prvi put klonirate repozitorijum, svi fajlovi će biti praćeni i nemodifikovani jer ste ih upravo čekautovali i još ništa niste izmenili.

Kako budete menjali fajlove, Git će primetiti da su modifikovani, jer ste ih promenili od poslednjeg komita. Ove fajlove ćete kasnije stejdžovati i onda ćete komitovati sve stejdžovane promene; tako se ciklus ponavlja.

Životni ciklus statusa fajlova.
Figure 8. Životni ciklus statusa fajlova.

Provera statusa fajlova

Glavni alat koji koristite da biste saznali koji fajlovi su u kom stanju je komanda git status. Ako pokrenete ovu komandu direktno posle kloniranja, videćete nešto ovako:

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working directory clean

Ovo znači da imate čist radni direktorijum - drugim rečima, nema praćenih i modifikovanih fajlova. Git takođe ne vidi nikakve nepraćene fajlove, inače bi bili izlistani ovde. Konačno, komanda vam kaže na kojoj se grani nalazite i informiše vas da nije divergirala sa iste grane na serveru. Zasad, grana će uvek biti master, što je podrazumevano; nećete se brinuti o tome ovde. Grananje u Gitu će razglabati o granama i referencama detaljnije.

Recimo da dodate novi fajl u projekat, običan README fajl. Ako fajl nije postojao ranije, a vi pokrenete git status, videćete svoje nepraćene fajlove na sledeći način:

$ echo 'My Project' > README
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Untracked files:
  (use "git add <file>..." to include in what will be committed)

    README

nothing added to commit but untracked files present (use "git add" to track)

Ovde vidite da je vaš novi README fajl nepraćen, jer je pod naslovom "Untracked files" u izveštaju. Nepraćeno u suštini znači da Git vidi fajl koji niste imali u prethodnom snimku (komitu); Git je neće uključiti u komitovane snimke dok mu vi eksplicitno ne naredite tako. Radi ovako da ne biste slučajno počeli da dodajete generisane binarne fajlove ili druge fajlove koje niste nameravali da dodate. Pošto želite da počnete da pratite README, treba dodati taj fajl.

Praćenje novih fajlova

Da biste počeli da pratite nov fajl, možete da koristite komandu git add. Praćenje README fajla počinje nakon pokretanja ove komande:

$ git add README

Ako ponovo pokrenete komandu status, videćete da je vaš README fajl sada praćen i stejdžovan za komit:

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   README

Vidi se da je fajl stejdžovan jer je pod naslovom "Changes to be committed". Ako sada komitujete, verzija fajla u trenutku kada ste pokrenuli git add komandu je ono što će se naći u istorijskom snimku. Možda se sećate da ste, kada ste ranije pokrenuli git init, pokrenuli i komandu git add <fajlovi>] — to je bilo da biste počeli da pratite fajlove u vašem direktorijumu. Komanda git add uzima putanju za fajl ili direktorijum; ako je direktorijum, onda komanda dodaje sve fajlove u tom direktorijumu koristeći rekurzivnu strategiju.

Stejdžovanje modifikovanih fajlova

Hajde da promenimo fajl koji je već praćen. Ako promenite ranije zapraćen fajl koji se zove CONTRIBUTING.md i onda pokrenete komandu git status ponovo, dobićete nešto ovako:

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   README

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   CONTRIBUTING.md

Fajl CONTRIBUTING.md se pojavljuje pod naslovom "Changes not staged for commit" — što znači da je fajl koji je praćen sada modifikovan u radnom direktorijumu ali još nije na stejdžu. Da biste ga stejdžovali, pokrenite komandu git add. git add je komanda za više namena - možete da je koristite da pratite nove fajlove, da stejdžujete fajlove, i da radite druge stvari kao što je obeležavanje da su konflikti kod fajlova do kojih je došlo prilikom spajanja razrešeni. Pomaže da o naredbi razmišljate kao "dodaj ovaj sadržaj u sledeći komit" a ne kao "dodaj ovaj fajl u projekat". Pokrenimo sada git add da stejdžujemo fajl CONTRIBUTING.md, a onda pokrenimo ponovo git status:

$ git add CONTRIBUTING.md
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   README
    modified:   CONTRIBUTING.md

Oba fajla su sada stejdžovana i spremna za sledeći komit. U ovom trenutku, pretpostavimo da ste se setili neke male izmene u CONTRIBUTING.md pre nego što ste komitovali. Otvarate fajl i pravite tu izmenu, i sada ste spremni za komit. Ipak, hajde da pokrenemo git status još jednom.

$ vim CONTRIBUTING.md
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   README
    modified:   CONTRIBUTING.md

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   CONTRIBUTING.md

Koji đavo? Sada je CONTRIBUTING.md izlistan i u stejdžovane i u nestejdžovane fajlove. Kako je to moguće? Ispostavlja se da Git stejdžuje fajl baš u trenutku kada pokrenete komandu git add. Ako komitujete sada, verzija CONTRIBUTING.md koja je bila kada ste pokrenuli komandu git add će ući u komit, a ne verzija koja se nalazi u radnom direktorijumu kada se pokrene git commit. Ako modifikujete fajl nakon što pokrenete git add, morate da pokrenete git add ponovo da biste stejdžovali poslednju verziju fajla:

$ git add CONTRIBUTING.md
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   README
    modified:   CONTRIBUTING.md

Kratki status

Mada je izlaz komande git status prilično sveobuhvatan, takođe je dosta rečit. Git ima i zastavicu za kratki status tako da promene možete da pregledate u kompaktnije zapisanom obliku. Ako pokrenete komandu git status -s ili git status --short dobijate mnogo pojednostavljeniji izlaz:

$ git status -s
 M README
MM Rakefile
A  lib/git.rb
M  lib/simplegit.rb
?? LICENSE.txt

Novi fajlovi koji nisu praćeni imaju ?? uz njih, novi fajlovi koji su dodati na stejdž imaju A (added), modifikovani fajlovi imaju M (modified) i tako dalje. Postoje dve kolone na izlazu - leva kolona stavlja do znanja da je fajl stejdžovan a desna kolona da je modifikovan. U gornjem primeru, fajl README je modifikovan i zatim stejdžovan. Fajl Rakefile je modifikovan, stejdžovan a zatim ponovo modifikovan, tako da postoje promene koje su stejdžovane, ali i one koje nisu.

Ignorisanje fajlova

Često ćete imati neku grupu fajlova koju ne želite da Git automatski dodaje ili da vam čak prikazuje kao nepraćene. To su obično automatski generisani fajlovi kao što su logovi ili fajlovi nastali bildovanjem. U tim slučajevima, možete da napravite obrasce za osluškivanje liste fajlova koji će ih gađati i onda ih staviti u .gitignore. Evo primera .gitignore fajla:

$ cat .gitignore
*.[oa]
*~

Prva linija govori Gitu da ignoriše sve fajlove koji se završavaju na .o ili .a - objektne i arhivne fajlove koji su proizvod bildovanja koda. Druga linija govori Gitu da ignoriše sve fajlove koji se završavaju tildom (~), koju koriste mnogi editori teksta kao što je Emacs da obeleži privremene fajlove. Možete da uključite i log, tmp ili pid direktorijum; automatski generisanu dokumentaciju; i tako dalje. Postavljanje .gitignore fajla pre nego što krenete sa radom je generalno dobra ideja jer tako nećete slučajno da komitujete fajlove koje ne želite u svom Git repozitorijumu.

Pravila za obrasce (paterne) koje možete da stavite u .gitignore fajl su sledeća:

  • prazne linije i linije koje počinju sa # se ignorišu,

  • standardni glob obrasci rade,

  • možete da počnete obrasce kosom crtom (/) da izbegnete rekurziju

  • možete da završite obrasce kosom crtom (/) da specificirate direktorijum,

  • možete da negirate obrazac tako što ćete ga početi znakom uzvika (!).

Glob obrasci su kao pojednostavljene regularne ekspresije koju koriste šelovi. Zvezdica (*) gađa jedan ili više karatera; [abc] gađa svaki karater u uglastim zagradama (u ovom slučaju a, b ili c); znak pitanja (?) gađa jedan karakter; a uglaste zagrade u kojima se nalaze karakteri razvojena crticom ([0-9]) gađaju bilo koji karakter između njih (u ovom slučaju od 0 do 9). Možete i da koristite dve zvezdice da gađate ugnježdene direktorijume; a/**/z bi gađalo a/z, a/b/z, a/b/c/z, i tako dalje.

Evo još jednog primera .gitignore fajla:

# bez .a fajlova
*.a

# ali prati lib.a, iako ignorišeš .a fajlove gore
!lib.a

# ignoriši samo TODO fajlove u trenutnom direktorijumu, ne i u poddir/TODO
/TODO

# ignoriši sve fajlove u build/ direktorijumu
build/

# ignoriši doc/notes.txt, ali ne doc/server/arch.txt
doc/*.txt

# ignoriši sve .pdf fajlove u doc/ direktorijumu (i njegovim poddirektorijumima)
doc/**/*.pdf
Tip

GitHub održava prilično sveobuhvatnu listu dobrih .gitignore primera fajlova za gomilu projekata i jezika na https://github.com/github/gitignore ako želite dobar početak za vaš projekat.

Gledanje stejdžovanih i nestejdžovanih promena

Ako vam je komanda git status previše nejasna — želite da znate tačno šta ste promenili, ne samo fajlove koje ste promenili - možete da koristite komandu git diff. Kasnije ćemo pokriti git diff malo detaljnije, ali verovatno ćete je najčešće koristiti da biste odgovorili na ova dva pitanja: Šta ste promenili ali još niste stejdžovali? I šta ste stejdžovali što ćete uskoro komitovati? Dok git status odgovara na ova pitanja veoma opšte tako što vam daje imena fajlova, git diff pokazuje tačne linije koje su dodate i uklonjene — kao da je zakrpa.

Recimo da editujete i stejdžujete README fajl ponovo i onda promenite fajl CONTRIBUTING.md ali ga ne stejdžujete. Ako pokrenete komandu git status, ponovo ćete videti nešto ovako:

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    modified:   README

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   CONTRIBUTING.md

Da biste videli šta se promenili ali još niste stejdžovali, ukucajte git diff bez drugih argumenata:

$ git diff
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 8ebb991..643e24f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -65,7 +65,8 @@ branch directly, things can get messy.
 Please include a nice description of your changes when you submit your PR;
 if we have to read the whole diff to figure out why you're contributing
 in the first place, you're less likely to get feedback and have your change
-merged in.
+merged in. Also, split your changes into comprehensive chunks if your patch is
+longer than a dozen lines.

 If you are starting to work on a particular area, feel free to submit a PR
 that highlights your work in progress (and note in the PR title that it's

Ova komanda poredi šta se nalazi u vašem radnom direktorijumu sa onim što je na stejdžu. Rezultat su promene koje ste promenili a još niste stejdžovali.

Ako želite da vidite šta ste stejdžovali, tj. šta će ući u sledeći komit, možete koristiti git diff --staged. Ova komanda poredi stejdžovane promene sa poslednjim komitom:

$ git diff --staged
diff --git a/README b/README
new file mode 100644
index 0000000..03902a1
--- /dev/null
+++ b/README
@@ -0,0 +1 @@
+My Project

Bitno je da obratite pažnju na to da git diff sam po sebi ne prikazuje sve promene koje ste napravili od poslednjeg komita — samo promene koje su još uvek nestejdžovane. Ovo može biti zbunjujuće, jer ako ste stejdžovali sve promene, git diff vam neće vratiti ništa.

Kao drugi primer, recimo da ste stejdžovali fajl CONTRIBUTING.md i onda ga editovali; sada možete iskoristiti git diff da vidite promene u fajlu koje su stejdžovane i promene koje su nestejdžovane. Ako naše okruženje izgleda ovako kao u donjem isečku,

$ git add CONTRIBUTING.md
$ echo '# test line' >> CONTRIBUTING.md
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    modified:   CONTRIBUTING.md

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   CONTRIBUTING.md

možete iskoristiti git diff da vidite šta je nestejdžovano

$ git diff
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 643e24f..87f08c8 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -119,3 +119,4 @@ at the
 ## Starter Projects

 See our [projects list](https://github.com/libgit2/libgit2/blob/development/PROJECTS.md).
+# test line

i git diff --cached da vidite šta ste dosad stejdžovali (--staged i --cached su sinonimi):

$ git diff --cached
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 8ebb991..643e24f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -65,7 +65,8 @@ branch directly, things can get messy.
 Please include a nice description of your changes when you submit your PR;
 if we have to read the whole diff to figure out why you're contributing
 in the first place, you're less likely to get feedback and have your change
-merged in.
+merged in. Also, split your changes into comprehensive chunks if your patch is
+longer than a dozen lines.

 If you are starting to work on a particular area, feel free to submit a PR
 that highlights your work in progress (and note in the PR title that it's
Note
Gitov diff kao eksterni alat

Nastavićemo da koristimo komandu git diff na razne načine kroz ostatak ove knjige. Postoji još jedan način da se pogledaju ove razlike ako preferirate grafički ili eksterni program za pregled razlika. Ako pokrenete git difftool umesto git diff, moći ćete da vidite ove razlike u softeru kao što je emerge, vimdiff i mnogim drugim (uključujući i komercijalne proizvode). Pokrenite git difftool -tool-help da vidite šta je dostupno za vaš sistem.

Komitovanje promena

Sada kad je stejdž namešten onako kako želite, možete da komitujete svoje promene. Upamtite da sve što još uvek nije stejdžovano — svi fajlovi koje ste kreirali ili modifikovali a niste pokrenuli git add nad njima od trenutka kada ste ih editovali — neće biti uključeni u ovaj komit. Oni će ostati kao modifikovani fajlovi na disku. U ovom slučaju, recimo da ste poslednji put kada ste pokrenuli git status videli da je sve stejdžovano, što znači da ste spremni da komitujete promene. Najjednostaniji način da komitujete je da ukucate git commit:

$ git commit

Kada uradite to, pokrenuće se editor koji ste izabrali. (Ovo je podešeno na osnovu promenljive okruženja $EDITOR u vašem šelu - obično vim ili emacs, mada možete da ga konfigurišete na šta god poželite pomoću komande git config --global core.editor kao što ste videli u Početak).

Editor prikazuje sledeći tekst (ovaj primer koristi editor Vim):

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Changes to be committed:
#	new file:   README
#	modified:   CONTRIBUTING.md
#
~
~
~
".git/COMMIT_EDITMSG" 9L, 283C

Vidite da podrazumevana komit poruka sadrži iskomentarisani poslednji izlaz komande git status i jednu praznu liniju na vrhu. Možete da obrišete ove komentare i upišete svoju poruku, ili možete da ih ostavite da bi vam pomogli da se prisetite šta komitujete. (Za još eksplicitniji podsednik onoga što ste modifikovali, možete da prosledite opciju -v komandi git commit. To će ubaciti razliku vaših promena u editor da biste mogli da vidite šta tačno komitujete.) Kada izađete iz editora, Git pravi vaš komit sa tom komit porukom (sa izbačenim komentarima i razlikom).

Alternativno, možete da ukucate komit poruku u inline modu sa komandom commit tako što ćete je specificirati nakon zastavice -m, na sledeći način:

$ git commit -m "Story 182: Fix benchmarks for speed"
[master 463dc4f] Story 182: Fix benchmarks for speed
 2 files changed, 2 insertions(+)
 create mode 100644 README

Sada ste napravili svoj prvi komit! Vidite da vam je komit dao neki izlaz o sebi: na kojoj ste grani komitovali (master), koju SHA-1 kontrolnu sumu ima komit (463dc4f), koliko fajlova je promenjeno, i statistiku o linijama koje su dodate i obrisane u komitu.

Upamtite da komit čuva snimak koji ste postavili na stejdž. Sve što niste stejdžovali i dalje stoji tamo modifikovano; možete da uradite još jedan komit da dodate to u istoriju. Svaki put kada uradite komit, pravite snimak projekta u tom stanju da biste kasnije mogli da se vratite na njega ili da ga poredite sa tim.

Preskakanje stejdža

Premda može biti veoma korisno da komitujete stvari tačno onako kako želite, stavljanje na stejdž ponekad ume da bude malo kompleksnije od onoga što želite da uradite. Ako želite da preskočite stejdž, Git nudi jednostavnu prečicu. Dodavanjem opcije -a komandi git commit, nalažete Gitu da automatski stejdžuje svaki fajl koji je već praćen pre nego što je urađen komit, što vam omogućava da preskočite deo git add:

$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   CONTRIBUTING.md

no changes added to commit (use "git add" and/or "git commit -a")
$ git commit -a -m 'added new benchmarks'
[master 83e38c7] added new benchmarks
 1 file changed, 5 insertions(+), 0 deletions(-)

Obratite pažnju na to da na ovaj način niste morali da pokrenete git add za CONTRIBUTING.md fajl pre nego što ste komitovali.

Uklanjanje fajlova

Da biste uklonili fajl iz Gita, morate da ga uklonite iz praćenih fajlova (tačnije, da ga sklonite sa stejdža) i onda komitujete. Komanda git rm radi to, i ona takođe uklanja fajl iz radnog direktorijuma kako ga ne biste videli među nepraćenim fajlovima sledeći put.

Ako samo uklonite fajl iz radnog direktorijuma, pojavljuje se pod "Changed but not updated" (odnosno, nestejdžovani) u izlazu komande git status.

$ rm PROJECTS.md
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        deleted:    PROJECTS.md

no changes added to commit (use "git add" and/or "git commit -a")

Ako onda pokrenete git rm, stejdžujete brisanje fajla:

$ git rm PROJECTS.md
rm 'PROJECTS.md'
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    deleted:    PROJECTS.md

Sledeći put kada komitujete, fajl će nestati i više neće biti praćen. Ako ste modifikovali fajl i već ga dodali u indeks, morate da primorate brisanje opcijom -f. Ovo je sigurnosna mera koja obezbeđuje da slučajno ne obrišete podatke koji još nisu sačuvani u snimku, jer takve Git ne može da vam vrati natrag.

Još jedna korisna stvar koju ćete možda želeti da uradite je da zadržite fajl u radnom stablu ali da ga obrišete sa stejdža. Drugim rečima, možda želite da zadržite fajl na hard disku ali ne želite da ga Git više prati. Ovo je posebno korisno ako ste zaboravili da dodate nešto u fajl .gitignore i slučajno ga stejdžovali, kao što je veliki log fajl ili gomila kompajliranih .a fajlova. Da biste uradili ovo, koristite --cached opciju:

$ git rm --cached README

Možete da prosleđujete fajlove, direktorijume i file-glob obrasce komandi git rm. To znači da možete da radite stvari kao:

$ git rm log/\*.log

Primetite naopaku kosu crtu (\) ispred *. Ovo je neophodno jer Git ima svoj filename expansion koji se dodaje na filename expansion vašeg šela. Ova komanda uklanja sve fajlove koji imaju .log ekstenziju u log/ direktorijumu. Ili, još jedan koristan primer bi bio

$ git rm \*~

čime uklanjate sve fajlove koji se završavaju tildom.

Pomeranje fajlova

Za razliku od mnogih drugih VCS sistema, Git ne prati pomeranje fajlova eksplicitno. Ako promenite ime fajlu u Gitu, nikakvi metapodaci se ne čuvaju u Gitu koji vam govore da je fajl promenio ime. Međutim, Git je prilično pametan što se tiče shvatanja toga nakon promene - malo ćemo se kasnije pozabaviti pomeranjem fajlova.

Imajući ovo u vidu, malo je konfuzno to što Git ima mv komandu. Ako želite da promenite ime fajlu u Gitu, možete da pokrenete nešto kao

$ git mv file_from file_to

i radiće kako treba. Zapravo, ako pokrenete nešto tako i onda pogledate status, videćete da Git to smatra fajlom čije je ime promenjeno:

$ git mv README.md README
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    README.md -> README

Ipak, ovo je ekvivalentno izvršenju sledećih komandi:

$ mv README.md README
$ git rm README.md
$ git add README

Git implicitno shvata da se radi o promeni imena fajlu, tako da nije važno da li ćete ime menjati na ovaj način ili sa mv komandom. Jedina prava razlika je to što je git mv jedna komanda umesto tri — udobnije je koristiti nju. Štaviše, možete da koristite bilo koji alat da promenite ime fajlu, a da kasnije primenite rm i add, pre nego što komitujete.